Age Owner Branch data TLA 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 : : #include "storage/read_stream.h"
21 : :
22 : :
23 : : /*
24 : : * Bulk deletion of all index entries pointing to a set of heap tuples.
25 : : * The set of target tuples is specified via a callback routine that tells
26 : : * whether any given heap tuple (identified by ItemPointer) is being deleted.
27 : : *
28 : : * Result: a palloc'd struct containing statistical info for VACUUM displays.
29 : : */
30 : : IndexBulkDeleteResult *
3635 teodor@sigaev.ru 31 :CBC 11 : blbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
32 : : IndexBulkDeleteCallback callback, void *callback_state)
33 : : {
34 : 11 : Relation index = info->index;
35 : : BlockNumber blkno,
36 : : npages;
37 : : FreeBlockNumberArray notFullPage;
38 : 11 : int countPage = 0;
39 : : BloomState state;
40 : : Buffer buffer;
41 : : Page page;
42 : : BloomMetaPageData *metaData;
43 : : GenericXLogState *gxlogState;
44 : : BlockRangeReadStreamPrivate p;
45 : : ReadStream *stream;
46 : :
47 [ + - ]: 11 : if (stats == NULL)
100 michael@paquier.xyz 48 :GNC 11 : stats = palloc0_object(IndexBulkDeleteResult);
49 : :
3635 teodor@sigaev.ru 50 :CBC 11 : initBloomState(&state, index);
51 : :
52 : : /*
53 : : * Iterate over the pages. We don't care about concurrently added pages,
54 : : * they can't contain tuples to delete.
55 : : */
56 : 11 : npages = RelationGetNumberOfBlocks(index);
57 : :
58 : : /* Scan all blocks except the metapage using streaming reads */
3 michael@paquier.xyz 59 :GNC 11 : p.current_blocknum = BLOOM_HEAD_BLKNO;
60 : 11 : p.last_exclusive = npages;
61 : :
62 : : /*
63 : : * It is safe to use batchmode as block_range_read_stream_cb takes no
64 : : * locks.
65 : : */
66 : 11 : stream = read_stream_begin_relation(READ_STREAM_MAINTENANCE |
67 : : READ_STREAM_FULL |
68 : : READ_STREAM_USE_BATCHING,
69 : : info->strategy,
70 : : index,
71 : : MAIN_FORKNUM,
72 : : block_range_read_stream_cb,
73 : : &p,
74 : : 0);
75 : :
3635 teodor@sigaev.ru 76 [ + + ]:CBC 678 : for (blkno = BLOOM_HEAD_BLKNO; blkno < npages; blkno++)
77 : : {
78 : : BloomTuple *itup,
79 : : *itupPtr,
80 : : *itupEnd;
81 : :
397 nathan@postgresql.or 82 : 667 : vacuum_delay_point(false);
83 : :
3 michael@paquier.xyz 84 :GNC 667 : buffer = read_stream_next_buffer(stream, NULL);
85 : :
3635 teodor@sigaev.ru 86 :CBC 667 : LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
87 : 667 : gxlogState = GenericXLogStart(index);
3624 tgl@sss.pgh.pa.us 88 : 667 : page = GenericXLogRegisterBuffer(gxlogState, buffer, 0);
89 : :
90 : : /* Ignore empty/deleted pages until blvacuumcleanup() */
3501 91 [ + - + + ]: 667 : if (PageIsNew(page) || BloomPageIsDeleted(page))
92 : : {
3635 teodor@sigaev.ru 93 : 4 : UnlockReleaseBuffer(buffer);
3634 94 : 4 : GenericXLogAbort(gxlogState);
3635 95 : 4 : continue;
96 : : }
97 : :
98 : : /*
99 : : * Iterate over the tuples. itup points to current tuple being
100 : : * scanned, itupPtr points to where to save next non-deleted tuple.
101 : : */
3634 102 : 663 : itup = itupPtr = BloomPageGetTuple(&state, page, FirstOffsetNumber);
103 : 663 : itupEnd = BloomPageGetTuple(&state, page,
104 : : OffsetNumberNext(BloomPageGetMaxOffset(page)));
3635 105 [ + + ]: 336663 : while (itup < itupEnd)
106 : : {
107 : : /* Do we have to delete this tuple? */
108 [ + + ]: 336000 : if (callback(&itup->heapPtr, callback_state))
109 : : {
110 : : /* Yes; adjust count of tuples that will be left on page */
111 : 48625 : BloomPageGetOpaque(page)->maxoff--;
3501 tgl@sss.pgh.pa.us 112 : 48625 : stats->tuples_removed += 1;
113 : : }
114 : : else
115 : : {
116 : : /* No; copy it to itupPtr++, but skip copy if not needed */
3635 teodor@sigaev.ru 117 [ + + ]: 287375 : if (itupPtr != itup)
102 peter@eisentraut.org 118 :GNC 284079 : memmove(itupPtr, itup, state.sizeOfBloomTuple);
3635 teodor@sigaev.ru 119 :CBC 287375 : itupPtr = BloomPageGetNextTuple(&state, itupPtr);
120 : : }
121 : :
122 : 336000 : itup = BloomPageGetNextTuple(&state, itup);
123 : : }
124 : :
125 : : /* Assert that we counted correctly */
3634 126 [ - + ]: 663 : Assert(itupPtr == BloomPageGetTuple(&state, page,
127 : : OffsetNumberNext(BloomPageGetMaxOffset(page))));
128 : :
129 : : /*
130 : : * Add page to new notFullPage list if we will not mark page as
131 : : * deleted and there is free space on it
132 : : */
133 [ + + ]: 663 : if (BloomPageGetMaxOffset(page) != 0 &&
3501 tgl@sss.pgh.pa.us 134 [ + + ]: 659 : BloomPageGetFreeSpace(&state, page) >= state.sizeOfBloomTuple &&
3635 teodor@sigaev.ru 135 [ + - ]: 656 : countPage < BloomMetaBlockN)
136 : 656 : notFullPage[countPage++] = blkno;
137 : :
138 : : /* Did we delete something? */
139 [ + + ]: 663 : if (itupPtr != itup)
140 : : {
141 : : /* Is it empty page now? */
3634 142 [ + + ]: 659 : if (BloomPageGetMaxOffset(page) == 0)
3635 143 : 4 : BloomPageSetDeleted(page);
144 : : /* Adjust pd_lower */
102 peter@eisentraut.org 145 :GNC 659 : ((PageHeader) page)->pd_lower = (char *) itupPtr - page;
146 : : /* Finish WAL-logging */
3635 teodor@sigaev.ru 147 :CBC 659 : GenericXLogFinish(gxlogState);
148 : : }
149 : : else
150 : : {
151 : : /* Didn't change anything: abort WAL-logging */
152 : 4 : GenericXLogAbort(gxlogState);
153 : : }
154 : 663 : UnlockReleaseBuffer(buffer);
155 : : }
156 : :
3 michael@paquier.xyz 157 [ - + ]:GNC 11 : Assert(read_stream_next_buffer(stream, NULL) == InvalidBuffer);
158 : 11 : read_stream_end(stream);
159 : :
160 : : /*
161 : : * Update the metapage's notFullPage list with whatever we found. Our
162 : : * info could already be out of date at this point, but blinsert() will
163 : : * cope if so.
164 : : */
3501 tgl@sss.pgh.pa.us 165 :CBC 11 : buffer = ReadBuffer(index, BLOOM_METAPAGE_BLKNO);
166 : 11 : LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
167 : :
168 : 11 : gxlogState = GenericXLogStart(index);
169 : 11 : page = GenericXLogRegisterBuffer(gxlogState, buffer, 0);
170 : :
171 : 11 : metaData = BloomPageGetMeta(page);
172 : 11 : memcpy(metaData->notFullPage, notFullPage, sizeof(BlockNumber) * countPage);
173 : 11 : metaData->nStart = 0;
174 : 11 : metaData->nEnd = countPage;
175 : :
176 : 11 : GenericXLogFinish(gxlogState);
177 : 11 : UnlockReleaseBuffer(buffer);
178 : :
3635 teodor@sigaev.ru 179 : 11 : return stats;
180 : : }
181 : :
182 : : /*
183 : : * Post-VACUUM cleanup.
184 : : *
185 : : * Result: a palloc'd struct containing statistical info for VACUUM displays.
186 : : */
187 : : IndexBulkDeleteResult *
188 : 12 : blvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats)
189 : : {
190 : 12 : Relation index = info->index;
191 : : BlockNumber npages,
192 : : blkno;
193 : : BlockRangeReadStreamPrivate p;
194 : : ReadStream *stream;
195 : :
196 [ - + ]: 12 : if (info->analyze_only)
3635 teodor@sigaev.ru 197 :UBC 0 : return stats;
198 : :
3635 teodor@sigaev.ru 199 [ + + ]:CBC 12 : if (stats == NULL)
100 michael@paquier.xyz 200 :GNC 1 : stats = palloc0_object(IndexBulkDeleteResult);
201 : :
202 : : /*
203 : : * Iterate over the pages: insert deleted pages into FSM and collect
204 : : * statistics.
205 : : */
3635 teodor@sigaev.ru 206 :CBC 12 : npages = RelationGetNumberOfBlocks(index);
3501 tgl@sss.pgh.pa.us 207 : 12 : stats->num_pages = npages;
208 : 12 : stats->pages_free = 0;
209 : 12 : stats->num_index_tuples = 0;
210 : :
211 : : /* Scan all blocks except the metapage using streaming reads */
3 michael@paquier.xyz 212 :GNC 12 : p.current_blocknum = BLOOM_HEAD_BLKNO;
213 : 12 : p.last_exclusive = npages;
214 : :
215 : : /*
216 : : * It is safe to use batchmode as block_range_read_stream_cb takes no
217 : : * locks.
218 : : */
219 : 12 : stream = read_stream_begin_relation(READ_STREAM_MAINTENANCE |
220 : : READ_STREAM_FULL |
221 : : READ_STREAM_USE_BATCHING,
222 : : info->strategy,
223 : : index,
224 : : MAIN_FORKNUM,
225 : : block_range_read_stream_cb,
226 : : &p,
227 : : 0);
228 : :
3635 teodor@sigaev.ru 229 [ + + ]:CBC 787 : for (blkno = BLOOM_HEAD_BLKNO; blkno < npages; blkno++)
230 : : {
231 : : Buffer buffer;
232 : : Page page;
233 : :
397 nathan@postgresql.or 234 : 775 : vacuum_delay_point(false);
235 : :
3 michael@paquier.xyz 236 :GNC 775 : buffer = read_stream_next_buffer(stream, NULL);
3635 teodor@sigaev.ru 237 :CBC 775 : LockBuffer(buffer, BUFFER_LOCK_SHARE);
198 peter@eisentraut.org 238 :GNC 775 : page = BufferGetPage(buffer);
239 : :
3501 tgl@sss.pgh.pa.us 240 [ + - + + ]:CBC 775 : if (PageIsNew(page) || BloomPageIsDeleted(page))
241 : : {
3635 teodor@sigaev.ru 242 : 8 : RecordFreeIndexPage(index, blkno);
3501 tgl@sss.pgh.pa.us 243 : 8 : stats->pages_free++;
244 : : }
245 : : else
246 : : {
3635 teodor@sigaev.ru 247 : 767 : stats->num_index_tuples += BloomPageGetMaxOffset(page);
248 : : }
249 : :
250 : 775 : UnlockReleaseBuffer(buffer);
251 : : }
252 : :
3 michael@paquier.xyz 253 [ - + ]:GNC 12 : Assert(read_stream_next_buffer(stream, NULL) == InvalidBuffer);
254 : 12 : read_stream_end(stream);
255 : :
3635 teodor@sigaev.ru 256 :CBC 12 : IndexFreeSpaceMapVacuum(info->index);
257 : :
258 : 12 : return stats;
259 : : }
|