Age Owner Branch data TLA 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 *
3546 teodor@sigaev.ru 30 :CBC 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)
11 michael@paquier.xyz 45 :GNC 11 : stats = palloc0_object(IndexBulkDeleteResult);
46 : :
3546 teodor@sigaev.ru 47 :CBC 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 : :
308 nathan@postgresql.or 60 : 667 : vacuum_delay_point(false);
61 : :
3546 teodor@sigaev.ru 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);
3535 tgl@sss.pgh.pa.us 67 : 667 : page = GenericXLogRegisterBuffer(gxlogState, buffer, 0);
68 : :
69 : : /* Ignore empty/deleted pages until blvacuumcleanup() */
3412 70 [ + - + + ]: 667 : if (PageIsNew(page) || BloomPageIsDeleted(page))
71 : : {
3546 teodor@sigaev.ru 72 : 4 : UnlockReleaseBuffer(buffer);
3545 73 : 4 : GenericXLogAbort(gxlogState);
3546 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 : : */
3545 81 : 663 : itup = itupPtr = BloomPageGetTuple(&state, page, FirstOffsetNumber);
82 : 663 : itupEnd = BloomPageGetTuple(&state, page,
83 : : OffsetNumberNext(BloomPageGetMaxOffset(page)));
3546 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--;
3412 tgl@sss.pgh.pa.us 91 : 48625 : stats->tuples_removed += 1;
92 : : }
93 : : else
94 : : {
95 : : /* No; copy it to itupPtr++, but skip copy if not needed */
3546 teodor@sigaev.ru 96 [ + + ]: 287375 : if (itupPtr != itup)
13 peter@eisentraut.org 97 :GNC 284079 : memmove(itupPtr, itup, state.sizeOfBloomTuple);
3546 teodor@sigaev.ru 98 :CBC 287375 : itupPtr = BloomPageGetNextTuple(&state, itupPtr);
99 : : }
100 : :
101 : 336000 : itup = BloomPageGetNextTuple(&state, itup);
102 : : }
103 : :
104 : : /* Assert that we counted correctly */
3545 105 [ - + ]: 663 : 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 &&
3412 tgl@sss.pgh.pa.us 113 [ + + ]: 659 : BloomPageGetFreeSpace(&state, page) >= state.sizeOfBloomTuple &&
3546 teodor@sigaev.ru 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? */
3545 121 [ + + ]: 659 : if (BloomPageGetMaxOffset(page) == 0)
3546 122 : 4 : BloomPageSetDeleted(page);
123 : : /* Adjust pd_lower */
13 peter@eisentraut.org 124 :GNC 659 : ((PageHeader) page)->pd_lower = (char *) itupPtr - page;
125 : : /* Finish WAL-logging */
3546 teodor@sigaev.ru 126 :CBC 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 : : */
3412 tgl@sss.pgh.pa.us 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 : :
3546 teodor@sigaev.ru 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)
3546 teodor@sigaev.ru 171 :UBC 0 : return stats;
172 : :
3546 teodor@sigaev.ru 173 [ + + ]:CBC 12 : if (stats == NULL)
11 michael@paquier.xyz 174 :GNC 1 : stats = palloc0_object(IndexBulkDeleteResult);
175 : :
176 : : /*
177 : : * Iterate over the pages: insert deleted pages into FSM and collect
178 : : * statistics.
179 : : */
3546 teodor@sigaev.ru 180 :CBC 12 : npages = RelationGetNumberOfBlocks(index);
3412 tgl@sss.pgh.pa.us 181 : 12 : stats->num_pages = npages;
182 : 12 : stats->pages_free = 0;
183 : 12 : stats->num_index_tuples = 0;
3546 teodor@sigaev.ru 184 [ + + ]: 787 : for (blkno = BLOOM_HEAD_BLKNO; blkno < npages; blkno++)
185 : : {
186 : : Buffer buffer;
187 : : Page page;
188 : :
308 nathan@postgresql.or 189 : 775 : vacuum_delay_point(false);
190 : :
3546 teodor@sigaev.ru 191 : 775 : buffer = ReadBufferExtended(index, MAIN_FORKNUM, blkno,
192 : : RBM_NORMAL, info->strategy);
193 : 775 : LockBuffer(buffer, BUFFER_LOCK_SHARE);
109 peter@eisentraut.org 194 :GNC 775 : page = BufferGetPage(buffer);
195 : :
3412 tgl@sss.pgh.pa.us 196 [ + - + + ]:CBC 775 : if (PageIsNew(page) || BloomPageIsDeleted(page))
197 : : {
3546 teodor@sigaev.ru 198 : 8 : RecordFreeIndexPage(index, blkno);
3412 tgl@sss.pgh.pa.us 199 : 8 : stats->pages_free++;
200 : : }
201 : : else
202 : : {
3546 teodor@sigaev.ru 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 : : }
|