1 /*-------------------------------------------------------------------------
4 * Bloom VACUUM functions.
6 * Copyright (c) 2016-2023, PostgreSQL Global Development Group
9 * contrib/bloom/blvacuum.c
11 *-------------------------------------------------------------------------
15 #include "access/genam.h"
17 #include "catalog/storage.h"
18 #include "commands/vacuum.h"
19 #include "miscadmin.h"
20 #include "postmaster/autovacuum.h"
21 #include "storage/bufmgr.h"
22 #include "storage/indexfsm.h"
23 #include "storage/lmgr.h"
27 * Bulk deletion of all index entries pointing to a set of heap tuples.
28 * The set of target tuples is specified via a callback routine that tells
29 * whether any given heap tuple (identified by ItemPointer) is being deleted.
31 * Result: a palloc'd struct containing statistical info for VACUUM displays.
33 IndexBulkDeleteResult
*
34 blbulkdelete(IndexVacuumInfo
*info
, IndexBulkDeleteResult
*stats
,
35 IndexBulkDeleteCallback callback
, void *callback_state
)
37 Relation index
= info
->index
;
40 FreeBlockNumberArray notFullPage
;
45 BloomMetaPageData
*metaData
;
46 GenericXLogState
*gxlogState
;
49 stats
= (IndexBulkDeleteResult
*) palloc0(sizeof(IndexBulkDeleteResult
));
51 initBloomState(&state
, index
);
54 * Iterate over the pages. We don't care about concurrently added pages,
55 * they can't contain tuples to delete.
57 npages
= RelationGetNumberOfBlocks(index
);
58 for (blkno
= BLOOM_HEAD_BLKNO
; blkno
< npages
; blkno
++)
66 buffer
= ReadBufferExtended(index
, MAIN_FORKNUM
, blkno
,
67 RBM_NORMAL
, info
->strategy
);
69 LockBuffer(buffer
, BUFFER_LOCK_EXCLUSIVE
);
70 gxlogState
= GenericXLogStart(index
);
71 page
= GenericXLogRegisterBuffer(gxlogState
, buffer
, 0);
73 /* Ignore empty/deleted pages until blvacuumcleanup() */
74 if (PageIsNew(page
) || BloomPageIsDeleted(page
))
76 UnlockReleaseBuffer(buffer
);
77 GenericXLogAbort(gxlogState
);
82 * Iterate over the tuples. itup points to current tuple being
83 * scanned, itupPtr points to where to save next non-deleted tuple.
85 itup
= itupPtr
= BloomPageGetTuple(&state
, page
, FirstOffsetNumber
);
86 itupEnd
= BloomPageGetTuple(&state
, page
,
87 OffsetNumberNext(BloomPageGetMaxOffset(page
)));
88 while (itup
< itupEnd
)
90 /* Do we have to delete this tuple? */
91 if (callback(&itup
->heapPtr
, callback_state
))
93 /* Yes; adjust count of tuples that will be left on page */
94 BloomPageGetOpaque(page
)->maxoff
--;
95 stats
->tuples_removed
+= 1;
99 /* No; copy it to itupPtr++, but skip copy if not needed */
101 memmove((Pointer
) itupPtr
, (Pointer
) itup
,
102 state
.sizeOfBloomTuple
);
103 itupPtr
= BloomPageGetNextTuple(&state
, itupPtr
);
106 itup
= BloomPageGetNextTuple(&state
, itup
);
109 /* Assert that we counted correctly */
110 Assert(itupPtr
== BloomPageGetTuple(&state
, page
,
111 OffsetNumberNext(BloomPageGetMaxOffset(page
))));
114 * Add page to new notFullPage list if we will not mark page as
115 * deleted and there is free space on it
117 if (BloomPageGetMaxOffset(page
) != 0 &&
118 BloomPageGetFreeSpace(&state
, page
) >= state
.sizeOfBloomTuple
&&
119 countPage
< BloomMetaBlockN
)
120 notFullPage
[countPage
++] = blkno
;
122 /* Did we delete something? */
125 /* Is it empty page now? */
126 if (BloomPageGetMaxOffset(page
) == 0)
127 BloomPageSetDeleted(page
);
128 /* Adjust pd_lower */
129 ((PageHeader
) page
)->pd_lower
= (Pointer
) itupPtr
- page
;
130 /* Finish WAL-logging */
131 GenericXLogFinish(gxlogState
);
135 /* Didn't change anything: abort WAL-logging */
136 GenericXLogAbort(gxlogState
);
138 UnlockReleaseBuffer(buffer
);
142 * Update the metapage's notFullPage list with whatever we found. Our
143 * info could already be out of date at this point, but blinsert() will
146 buffer
= ReadBuffer(index
, BLOOM_METAPAGE_BLKNO
);
147 LockBuffer(buffer
, BUFFER_LOCK_EXCLUSIVE
);
149 gxlogState
= GenericXLogStart(index
);
150 page
= GenericXLogRegisterBuffer(gxlogState
, buffer
, 0);
152 metaData
= BloomPageGetMeta(page
);
153 memcpy(metaData
->notFullPage
, notFullPage
, sizeof(BlockNumber
) * countPage
);
154 metaData
->nStart
= 0;
155 metaData
->nEnd
= countPage
;
157 GenericXLogFinish(gxlogState
);
158 UnlockReleaseBuffer(buffer
);
164 * Post-VACUUM cleanup.
166 * Result: a palloc'd struct containing statistical info for VACUUM displays.
168 IndexBulkDeleteResult
*
169 blvacuumcleanup(IndexVacuumInfo
*info
, IndexBulkDeleteResult
*stats
)
171 Relation index
= info
->index
;
175 if (info
->analyze_only
)
179 stats
= (IndexBulkDeleteResult
*) palloc0(sizeof(IndexBulkDeleteResult
));
182 * Iterate over the pages: insert deleted pages into FSM and collect
185 npages
= RelationGetNumberOfBlocks(index
);
186 stats
->num_pages
= npages
;
187 stats
->pages_free
= 0;
188 stats
->num_index_tuples
= 0;
189 for (blkno
= BLOOM_HEAD_BLKNO
; blkno
< npages
; blkno
++)
194 vacuum_delay_point();
196 buffer
= ReadBufferExtended(index
, MAIN_FORKNUM
, blkno
,
197 RBM_NORMAL
, info
->strategy
);
198 LockBuffer(buffer
, BUFFER_LOCK_SHARE
);
199 page
= (Page
) BufferGetPage(buffer
);
201 if (PageIsNew(page
) || BloomPageIsDeleted(page
))
203 RecordFreeIndexPage(index
, blkno
);
208 stats
->num_index_tuples
+= BloomPageGetMaxOffset(page
);
211 UnlockReleaseBuffer(buffer
);
214 IndexFreeSpaceMapVacuum(info
->index
);