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