Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * blvacuum.c
4 : * Bloom VACUUM functions.
5 : *
6 : * Copyright (c) 2016-2024, 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 = (IndexBulkDeleteResult *) palloc0(sizeof(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();
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((Pointer) itupPtr, (Pointer) itup,
98 : state.sizeOfBloomTuple);
99 574750 : itupPtr = BloomPageGetNextTuple(&state, itupPtr);
100 : }
101 :
102 672000 : itup = BloomPageGetNextTuple(&state, itup);
103 : }
104 :
105 : /* Assert that we counted correctly */
106 : Assert(itupPtr == BloomPageGetTuple(&state, page,
107 : OffsetNumberNext(BloomPageGetMaxOffset(page))));
108 :
109 : /*
110 : * Add page to new notFullPage list if we will not mark page as
111 : * deleted and there is free space on it
112 : */
113 1326 : if (BloomPageGetMaxOffset(page) != 0 &&
114 1318 : BloomPageGetFreeSpace(&state, page) >= state.sizeOfBloomTuple &&
115 1312 : countPage < BloomMetaBlockN)
116 1312 : notFullPage[countPage++] = blkno;
117 :
118 : /* Did we delete something? */
119 1326 : if (itupPtr != itup)
120 : {
121 : /* Is it empty page now? */
122 1318 : if (BloomPageGetMaxOffset(page) == 0)
123 8 : BloomPageSetDeleted(page);
124 : /* Adjust pd_lower */
125 1318 : ((PageHeader) page)->pd_lower = (Pointer) itupPtr - page;
126 : /* Finish WAL-logging */
127 1318 : GenericXLogFinish(gxlogState);
128 : }
129 : else
130 : {
131 : /* Didn't change anything: abort WAL-logging */
132 8 : GenericXLogAbort(gxlogState);
133 : }
134 1326 : UnlockReleaseBuffer(buffer);
135 : }
136 :
137 : /*
138 : * Update the metapage's notFullPage list with whatever we found. Our
139 : * info could already be out of date at this point, but blinsert() will
140 : * cope if so.
141 : */
142 22 : buffer = ReadBuffer(index, BLOOM_METAPAGE_BLKNO);
143 22 : LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
144 :
145 22 : gxlogState = GenericXLogStart(index);
146 22 : page = GenericXLogRegisterBuffer(gxlogState, buffer, 0);
147 :
148 22 : metaData = BloomPageGetMeta(page);
149 22 : memcpy(metaData->notFullPage, notFullPage, sizeof(BlockNumber) * countPage);
150 22 : metaData->nStart = 0;
151 22 : metaData->nEnd = countPage;
152 :
153 22 : GenericXLogFinish(gxlogState);
154 22 : UnlockReleaseBuffer(buffer);
155 :
156 22 : return stats;
157 : }
158 :
159 : /*
160 : * Post-VACUUM cleanup.
161 : *
162 : * Result: a palloc'd struct containing statistical info for VACUUM displays.
163 : */
164 : IndexBulkDeleteResult *
165 24 : blvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats)
166 : {
167 24 : Relation index = info->index;
168 : BlockNumber npages,
169 : blkno;
170 :
171 24 : if (info->analyze_only)
172 0 : return stats;
173 :
174 24 : if (stats == NULL)
175 2 : stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
176 :
177 : /*
178 : * Iterate over the pages: insert deleted pages into FSM and collect
179 : * statistics.
180 : */
181 24 : npages = RelationGetNumberOfBlocks(index);
182 24 : stats->num_pages = npages;
183 24 : stats->pages_free = 0;
184 24 : stats->num_index_tuples = 0;
185 1574 : for (blkno = BLOOM_HEAD_BLKNO; blkno < npages; blkno++)
186 : {
187 : Buffer buffer;
188 : Page page;
189 :
190 1550 : vacuum_delay_point();
191 :
192 1550 : buffer = ReadBufferExtended(index, MAIN_FORKNUM, blkno,
193 : RBM_NORMAL, info->strategy);
194 1550 : LockBuffer(buffer, BUFFER_LOCK_SHARE);
195 1550 : page = (Page) BufferGetPage(buffer);
196 :
197 1550 : if (PageIsNew(page) || BloomPageIsDeleted(page))
198 : {
199 16 : RecordFreeIndexPage(index, blkno);
200 16 : stats->pages_free++;
201 : }
202 : else
203 : {
204 1534 : stats->num_index_tuples += BloomPageGetMaxOffset(page);
205 : }
206 :
207 1550 : UnlockReleaseBuffer(buffer);
208 : }
209 :
210 24 : IndexFreeSpaceMapVacuum(info->index);
211 :
212 24 : return stats;
213 : }
|