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 : }
|