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 *
3445 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)
45 : 11 : stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
46 : :
47 : 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 : :
207 nathan@postgresql.or 60 : 667 : vacuum_delay_point(false);
61 : :
3445 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);
3434 tgl@sss.pgh.pa.us 67 : 667 : page = GenericXLogRegisterBuffer(gxlogState, buffer, 0);
68 : :
69 : : /* Ignore empty/deleted pages until blvacuumcleanup() */
3311 70 [ + - + + ]: 667 : if (PageIsNew(page) || BloomPageIsDeleted(page))
71 : : {
3445 teodor@sigaev.ru 72 : 4 : UnlockReleaseBuffer(buffer);
3444 73 : 4 : GenericXLogAbort(gxlogState);
3445 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 : : */
3444 81 : 663 : itup = itupPtr = BloomPageGetTuple(&state, page, FirstOffsetNumber);
82 : 663 : itupEnd = BloomPageGetTuple(&state, page,
83 : : OffsetNumberNext(BloomPageGetMaxOffset(page)));
3445 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--;
3311 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 */
3445 teodor@sigaev.ru 96 [ + + ]: 287375 : if (itupPtr != itup)
97 : 284079 : memmove((Pointer) itupPtr, (Pointer) itup,
98 : : state.sizeOfBloomTuple);
99 : 287375 : itupPtr = BloomPageGetNextTuple(&state, itupPtr);
100 : : }
101 : :
102 : 336000 : itup = BloomPageGetNextTuple(&state, itup);
103 : : }
104 : :
105 : : /* Assert that we counted correctly */
3444 106 [ - + ]: 663 : 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 [ + + ]: 663 : if (BloomPageGetMaxOffset(page) != 0 &&
3311 tgl@sss.pgh.pa.us 114 [ + + ]: 659 : BloomPageGetFreeSpace(&state, page) >= state.sizeOfBloomTuple &&
3445 teodor@sigaev.ru 115 [ + - ]: 656 : countPage < BloomMetaBlockN)
116 : 656 : notFullPage[countPage++] = blkno;
117 : :
118 : : /* Did we delete something? */
119 [ + + ]: 663 : if (itupPtr != itup)
120 : : {
121 : : /* Is it empty page now? */
3444 122 [ + + ]: 659 : if (BloomPageGetMaxOffset(page) == 0)
3445 123 : 4 : BloomPageSetDeleted(page);
124 : : /* Adjust pd_lower */
125 : 659 : ((PageHeader) page)->pd_lower = (Pointer) itupPtr - page;
126 : : /* Finish WAL-logging */
127 : 659 : GenericXLogFinish(gxlogState);
128 : : }
129 : : else
130 : : {
131 : : /* Didn't change anything: abort WAL-logging */
132 : 4 : GenericXLogAbort(gxlogState);
133 : : }
134 : 663 : 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 : : */
3311 tgl@sss.pgh.pa.us 142 : 11 : buffer = ReadBuffer(index, BLOOM_METAPAGE_BLKNO);
143 : 11 : LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
144 : :
145 : 11 : gxlogState = GenericXLogStart(index);
146 : 11 : page = GenericXLogRegisterBuffer(gxlogState, buffer, 0);
147 : :
148 : 11 : metaData = BloomPageGetMeta(page);
149 : 11 : memcpy(metaData->notFullPage, notFullPage, sizeof(BlockNumber) * countPage);
150 : 11 : metaData->nStart = 0;
151 : 11 : metaData->nEnd = countPage;
152 : :
153 : 11 : GenericXLogFinish(gxlogState);
154 : 11 : UnlockReleaseBuffer(buffer);
155 : :
3445 teodor@sigaev.ru 156 : 11 : 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 : 12 : blvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats)
166 : : {
167 : 12 : Relation index = info->index;
168 : : BlockNumber npages,
169 : : blkno;
170 : :
171 [ - + ]: 12 : if (info->analyze_only)
3445 teodor@sigaev.ru 172 :UBC 0 : return stats;
173 : :
3445 teodor@sigaev.ru 174 [ + + ]:CBC 12 : if (stats == NULL)
175 : 1 : stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
176 : :
177 : : /*
178 : : * Iterate over the pages: insert deleted pages into FSM and collect
179 : : * statistics.
180 : : */
181 : 12 : npages = RelationGetNumberOfBlocks(index);
3311 tgl@sss.pgh.pa.us 182 : 12 : stats->num_pages = npages;
183 : 12 : stats->pages_free = 0;
184 : 12 : stats->num_index_tuples = 0;
3445 teodor@sigaev.ru 185 [ + + ]: 787 : for (blkno = BLOOM_HEAD_BLKNO; blkno < npages; blkno++)
186 : : {
187 : : Buffer buffer;
188 : : Page page;
189 : :
207 nathan@postgresql.or 190 : 775 : vacuum_delay_point(false);
191 : :
3445 teodor@sigaev.ru 192 : 775 : buffer = ReadBufferExtended(index, MAIN_FORKNUM, blkno,
193 : : RBM_NORMAL, info->strategy);
194 : 775 : LockBuffer(buffer, BUFFER_LOCK_SHARE);
8 peter@eisentraut.org 195 :GNC 775 : page = BufferGetPage(buffer);
196 : :
3311 tgl@sss.pgh.pa.us 197 [ + - + + ]:CBC 775 : if (PageIsNew(page) || BloomPageIsDeleted(page))
198 : : {
3445 teodor@sigaev.ru 199 : 8 : RecordFreeIndexPage(index, blkno);
3311 tgl@sss.pgh.pa.us 200 : 8 : stats->pages_free++;
201 : : }
202 : : else
203 : : {
3445 teodor@sigaev.ru 204 : 767 : stats->num_index_tuples += BloomPageGetMaxOffset(page);
205 : : }
206 : :
207 : 775 : UnlockReleaseBuffer(buffer);
208 : : }
209 : :
210 : 12 : IndexFreeSpaceMapVacuum(info->index);
211 : :
212 : 12 : return stats;
213 : : }
|