LCOV - code coverage report
Current view: top level - contrib/bloom - blvacuum.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 98.6 % 71 70
Test Date: 2026-03-03 18:14:56 Functions: 100.0 % 2 2
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*-------------------------------------------------------------------------
       2              :  *
       3              :  * blvacuum.c
       4              :  *      Bloom VACUUM functions.
       5              :  *
       6              :  * Copyright (c) 2016-2026, PostgreSQL Global Development Group
       7              :  *
       8              :  * IDENTIFICATION
       9              :  *    contrib/bloom/blvacuum.c
      10              :  *
      11              :  *-------------------------------------------------------------------------
      12              :  */
      13              : #include "postgres.h"
      14              : 
      15              : #include "access/genam.h"
      16              : #include "bloom.h"
      17              : #include "commands/vacuum.h"
      18              : #include "storage/bufmgr.h"
      19              : #include "storage/indexfsm.h"
      20              : 
      21              : 
      22              : /*
      23              :  * Bulk deletion of all index entries pointing to a set of heap tuples.
      24              :  * The set of target tuples is specified via a callback routine that tells
      25              :  * whether any given heap tuple (identified by ItemPointer) is being deleted.
      26              :  *
      27              :  * Result: a palloc'd struct containing statistical info for VACUUM displays.
      28              :  */
      29              : IndexBulkDeleteResult *
      30           11 : blbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
      31              :              IndexBulkDeleteCallback callback, void *callback_state)
      32              : {
      33           11 :     Relation    index = info->index;
      34              :     BlockNumber blkno,
      35              :                 npages;
      36              :     FreeBlockNumberArray notFullPage;
      37           11 :     int         countPage = 0;
      38              :     BloomState  state;
      39              :     Buffer      buffer;
      40              :     Page        page;
      41              :     BloomMetaPageData *metaData;
      42              :     GenericXLogState *gxlogState;
      43              : 
      44           11 :     if (stats == NULL)
      45           11 :         stats = palloc0_object(IndexBulkDeleteResult);
      46              : 
      47           11 :     initBloomState(&state, index);
      48              : 
      49              :     /*
      50              :      * Iterate over the pages. We don't care about concurrently added pages,
      51              :      * they can't contain tuples to delete.
      52              :      */
      53           11 :     npages = RelationGetNumberOfBlocks(index);
      54          678 :     for (blkno = BLOOM_HEAD_BLKNO; blkno < npages; blkno++)
      55              :     {
      56              :         BloomTuple *itup,
      57              :                    *itupPtr,
      58              :                    *itupEnd;
      59              : 
      60          667 :         vacuum_delay_point(false);
      61              : 
      62          667 :         buffer = ReadBufferExtended(index, MAIN_FORKNUM, blkno,
      63              :                                     RBM_NORMAL, info->strategy);
      64              : 
      65          667 :         LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
      66          667 :         gxlogState = GenericXLogStart(index);
      67          667 :         page = GenericXLogRegisterBuffer(gxlogState, buffer, 0);
      68              : 
      69              :         /* Ignore empty/deleted pages until blvacuumcleanup() */
      70          667 :         if (PageIsNew(page) || BloomPageIsDeleted(page))
      71              :         {
      72            4 :             UnlockReleaseBuffer(buffer);
      73            4 :             GenericXLogAbort(gxlogState);
      74            4 :             continue;
      75              :         }
      76              : 
      77              :         /*
      78              :          * Iterate over the tuples.  itup points to current tuple being
      79              :          * scanned, itupPtr points to where to save next non-deleted tuple.
      80              :          */
      81          663 :         itup = itupPtr = BloomPageGetTuple(&state, page, FirstOffsetNumber);
      82          663 :         itupEnd = BloomPageGetTuple(&state, page,
      83              :                                     OffsetNumberNext(BloomPageGetMaxOffset(page)));
      84       336663 :         while (itup < itupEnd)
      85              :         {
      86              :             /* Do we have to delete this tuple? */
      87       336000 :             if (callback(&itup->heapPtr, callback_state))
      88              :             {
      89              :                 /* Yes; adjust count of tuples that will be left on page */
      90        48625 :                 BloomPageGetOpaque(page)->maxoff--;
      91        48625 :                 stats->tuples_removed += 1;
      92              :             }
      93              :             else
      94              :             {
      95              :                 /* No; copy it to itupPtr++, but skip copy if not needed */
      96       287375 :                 if (itupPtr != itup)
      97       284079 :                     memmove(itupPtr, itup, state.sizeOfBloomTuple);
      98       287375 :                 itupPtr = BloomPageGetNextTuple(&state, itupPtr);
      99              :             }
     100              : 
     101       336000 :             itup = BloomPageGetNextTuple(&state, itup);
     102              :         }
     103              : 
     104              :         /* Assert that we counted correctly */
     105              :         Assert(itupPtr == BloomPageGetTuple(&state, page,
     106              :                                             OffsetNumberNext(BloomPageGetMaxOffset(page))));
     107              : 
     108              :         /*
     109              :          * Add page to new notFullPage list if we will not mark page as
     110              :          * deleted and there is free space on it
     111              :          */
     112          663 :         if (BloomPageGetMaxOffset(page) != 0 &&
     113          659 :             BloomPageGetFreeSpace(&state, page) >= state.sizeOfBloomTuple &&
     114          656 :             countPage < BloomMetaBlockN)
     115          656 :             notFullPage[countPage++] = blkno;
     116              : 
     117              :         /* Did we delete something? */
     118          663 :         if (itupPtr != itup)
     119              :         {
     120              :             /* Is it empty page now? */
     121          659 :             if (BloomPageGetMaxOffset(page) == 0)
     122            4 :                 BloomPageSetDeleted(page);
     123              :             /* Adjust pd_lower */
     124          659 :             ((PageHeader) page)->pd_lower = (char *) itupPtr - page;
     125              :             /* Finish WAL-logging */
     126          659 :             GenericXLogFinish(gxlogState);
     127              :         }
     128              :         else
     129              :         {
     130              :             /* Didn't change anything: abort WAL-logging */
     131            4 :             GenericXLogAbort(gxlogState);
     132              :         }
     133          663 :         UnlockReleaseBuffer(buffer);
     134              :     }
     135              : 
     136              :     /*
     137              :      * Update the metapage's notFullPage list with whatever we found.  Our
     138              :      * info could already be out of date at this point, but blinsert() will
     139              :      * cope if so.
     140              :      */
     141           11 :     buffer = ReadBuffer(index, BLOOM_METAPAGE_BLKNO);
     142           11 :     LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
     143              : 
     144           11 :     gxlogState = GenericXLogStart(index);
     145           11 :     page = GenericXLogRegisterBuffer(gxlogState, buffer, 0);
     146              : 
     147           11 :     metaData = BloomPageGetMeta(page);
     148           11 :     memcpy(metaData->notFullPage, notFullPage, sizeof(BlockNumber) * countPage);
     149           11 :     metaData->nStart = 0;
     150           11 :     metaData->nEnd = countPage;
     151              : 
     152           11 :     GenericXLogFinish(gxlogState);
     153           11 :     UnlockReleaseBuffer(buffer);
     154              : 
     155           11 :     return stats;
     156              : }
     157              : 
     158              : /*
     159              :  * Post-VACUUM cleanup.
     160              :  *
     161              :  * Result: a palloc'd struct containing statistical info for VACUUM displays.
     162              :  */
     163              : IndexBulkDeleteResult *
     164           12 : blvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats)
     165              : {
     166           12 :     Relation    index = info->index;
     167              :     BlockNumber npages,
     168              :                 blkno;
     169              : 
     170           12 :     if (info->analyze_only)
     171            0 :         return stats;
     172              : 
     173           12 :     if (stats == NULL)
     174            1 :         stats = palloc0_object(IndexBulkDeleteResult);
     175              : 
     176              :     /*
     177              :      * Iterate over the pages: insert deleted pages into FSM and collect
     178              :      * statistics.
     179              :      */
     180           12 :     npages = RelationGetNumberOfBlocks(index);
     181           12 :     stats->num_pages = npages;
     182           12 :     stats->pages_free = 0;
     183           12 :     stats->num_index_tuples = 0;
     184          787 :     for (blkno = BLOOM_HEAD_BLKNO; blkno < npages; blkno++)
     185              :     {
     186              :         Buffer      buffer;
     187              :         Page        page;
     188              : 
     189          775 :         vacuum_delay_point(false);
     190              : 
     191          775 :         buffer = ReadBufferExtended(index, MAIN_FORKNUM, blkno,
     192              :                                     RBM_NORMAL, info->strategy);
     193          775 :         LockBuffer(buffer, BUFFER_LOCK_SHARE);
     194          775 :         page = BufferGetPage(buffer);
     195              : 
     196          775 :         if (PageIsNew(page) || BloomPageIsDeleted(page))
     197              :         {
     198            8 :             RecordFreeIndexPage(index, blkno);
     199            8 :             stats->pages_free++;
     200              :         }
     201              :         else
     202              :         {
     203          767 :             stats->num_index_tuples += BloomPageGetMaxOffset(page);
     204              :         }
     205              : 
     206          775 :         UnlockReleaseBuffer(buffer);
     207              :     }
     208              : 
     209           12 :     IndexFreeSpaceMapVacuum(info->index);
     210              : 
     211           12 :     return stats;
     212              : }
        

Generated by: LCOV version 2.0-1