LCOV - code coverage report
Current view: top level - contrib/bloom - blvacuum.c (source / functions) Hit Total Coverage
Test: PostgreSQL 19devel Lines: 70 71 98.6 %
Date: 2025-12-15 22:17:34 Functions: 2 2 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * blvacuum.c
       4             :  *      Bloom VACUUM functions.
       5             :  *
       6             :  * Copyright (c) 2016-2025, 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          22 : blbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
      31             :              IndexBulkDeleteCallback callback, void *callback_state)
      32             : {
      33          22 :     Relation    index = info->index;
      34             :     BlockNumber blkno,
      35             :                 npages;
      36             :     FreeBlockNumberArray notFullPage;
      37          22 :     int         countPage = 0;
      38             :     BloomState  state;
      39             :     Buffer      buffer;
      40             :     Page        page;
      41             :     BloomMetaPageData *metaData;
      42             :     GenericXLogState *gxlogState;
      43             : 
      44          22 :     if (stats == NULL)
      45          22 :         stats = palloc0_object(IndexBulkDeleteResult);
      46             : 
      47          22 :     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          22 :     npages = RelationGetNumberOfBlocks(index);
      54        1356 :     for (blkno = BLOOM_HEAD_BLKNO; blkno < npages; blkno++)
      55             :     {
      56             :         BloomTuple *itup,
      57             :                    *itupPtr,
      58             :                    *itupEnd;
      59             : 
      60        1334 :         vacuum_delay_point(false);
      61             : 
      62        1334 :         buffer = ReadBufferExtended(index, MAIN_FORKNUM, blkno,
      63             :                                     RBM_NORMAL, info->strategy);
      64             : 
      65        1334 :         LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
      66        1334 :         gxlogState = GenericXLogStart(index);
      67        1334 :         page = GenericXLogRegisterBuffer(gxlogState, buffer, 0);
      68             : 
      69             :         /* Ignore empty/deleted pages until blvacuumcleanup() */
      70        1334 :         if (PageIsNew(page) || BloomPageIsDeleted(page))
      71             :         {
      72           8 :             UnlockReleaseBuffer(buffer);
      73           8 :             GenericXLogAbort(gxlogState);
      74           8 :             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        1326 :         itup = itupPtr = BloomPageGetTuple(&state, page, FirstOffsetNumber);
      82        1326 :         itupEnd = BloomPageGetTuple(&state, page,
      83             :                                     OffsetNumberNext(BloomPageGetMaxOffset(page)));
      84      673326 :         while (itup < itupEnd)
      85             :         {
      86             :             /* Do we have to delete this tuple? */
      87      672000 :             if (callback(&itup->heapPtr, callback_state))
      88             :             {
      89             :                 /* Yes; adjust count of tuples that will be left on page */
      90       97250 :                 BloomPageGetOpaque(page)->maxoff--;
      91       97250 :                 stats->tuples_removed += 1;
      92             :             }
      93             :             else
      94             :             {
      95             :                 /* No; copy it to itupPtr++, but skip copy if not needed */
      96      574750 :                 if (itupPtr != itup)
      97      568158 :                     memmove(itupPtr, itup, state.sizeOfBloomTuple);
      98      574750 :                 itupPtr = BloomPageGetNextTuple(&state, itupPtr);
      99             :             }
     100             : 
     101      672000 :             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        1326 :         if (BloomPageGetMaxOffset(page) != 0 &&
     113        1318 :             BloomPageGetFreeSpace(&state, page) >= state.sizeOfBloomTuple &&
     114        1312 :             countPage < BloomMetaBlockN)
     115        1312 :             notFullPage[countPage++] = blkno;
     116             : 
     117             :         /* Did we delete something? */
     118        1326 :         if (itupPtr != itup)
     119             :         {
     120             :             /* Is it empty page now? */
     121        1318 :             if (BloomPageGetMaxOffset(page) == 0)
     122           8 :                 BloomPageSetDeleted(page);
     123             :             /* Adjust pd_lower */
     124        1318 :             ((PageHeader) page)->pd_lower = (char *) itupPtr - page;
     125             :             /* Finish WAL-logging */
     126        1318 :             GenericXLogFinish(gxlogState);
     127             :         }
     128             :         else
     129             :         {
     130             :             /* Didn't change anything: abort WAL-logging */
     131           8 :             GenericXLogAbort(gxlogState);
     132             :         }
     133        1326 :         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          22 :     buffer = ReadBuffer(index, BLOOM_METAPAGE_BLKNO);
     142          22 :     LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
     143             : 
     144          22 :     gxlogState = GenericXLogStart(index);
     145          22 :     page = GenericXLogRegisterBuffer(gxlogState, buffer, 0);
     146             : 
     147          22 :     metaData = BloomPageGetMeta(page);
     148          22 :     memcpy(metaData->notFullPage, notFullPage, sizeof(BlockNumber) * countPage);
     149          22 :     metaData->nStart = 0;
     150          22 :     metaData->nEnd = countPage;
     151             : 
     152          22 :     GenericXLogFinish(gxlogState);
     153          22 :     UnlockReleaseBuffer(buffer);
     154             : 
     155          22 :     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          24 : blvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats)
     165             : {
     166          24 :     Relation    index = info->index;
     167             :     BlockNumber npages,
     168             :                 blkno;
     169             : 
     170          24 :     if (info->analyze_only)
     171           0 :         return stats;
     172             : 
     173          24 :     if (stats == NULL)
     174           2 :         stats = palloc0_object(IndexBulkDeleteResult);
     175             : 
     176             :     /*
     177             :      * Iterate over the pages: insert deleted pages into FSM and collect
     178             :      * statistics.
     179             :      */
     180          24 :     npages = RelationGetNumberOfBlocks(index);
     181          24 :     stats->num_pages = npages;
     182          24 :     stats->pages_free = 0;
     183          24 :     stats->num_index_tuples = 0;
     184        1574 :     for (blkno = BLOOM_HEAD_BLKNO; blkno < npages; blkno++)
     185             :     {
     186             :         Buffer      buffer;
     187             :         Page        page;
     188             : 
     189        1550 :         vacuum_delay_point(false);
     190             : 
     191        1550 :         buffer = ReadBufferExtended(index, MAIN_FORKNUM, blkno,
     192             :                                     RBM_NORMAL, info->strategy);
     193        1550 :         LockBuffer(buffer, BUFFER_LOCK_SHARE);
     194        1550 :         page = BufferGetPage(buffer);
     195             : 
     196        1550 :         if (PageIsNew(page) || BloomPageIsDeleted(page))
     197             :         {
     198          16 :             RecordFreeIndexPage(index, blkno);
     199          16 :             stats->pages_free++;
     200             :         }
     201             :         else
     202             :         {
     203        1534 :             stats->num_index_tuples += BloomPageGetMaxOffset(page);
     204             :         }
     205             : 
     206        1550 :         UnlockReleaseBuffer(buffer);
     207             :     }
     208             : 
     209          24 :     IndexFreeSpaceMapVacuum(info->index);
     210             : 
     211          24 :     return stats;
     212             : }

Generated by: LCOV version 1.16