2424#include "storage/lmgr.h"
2525#include "utils/memutils.h"
2626
27- /*
28- * State kept across vacuum stages.
29- */
27+ /* Working state needed by gistbulkdelete */
3028typedef struct
3129{
32- IndexBulkDeleteResult stats ;/* must be first */
30+ IndexVacuumInfo * info ;
31+ IndexBulkDeleteResult * stats ;
32+ IndexBulkDeleteCallback callback ;
33+ void * callback_state ;
34+ GistNSN startNSN ;
3335
3436/*
35- * These are used to memorize all internal and empty leaf pages in the 1st
36- * vacuum stage. They are used in the 2nd stage, to delete all the empty
37- * pages.
37+ * These are used to memorize all internal and empty leaf pages. They are
38+ * used for deleting all the empty pages.
3839 */
3940IntegerSet * internal_page_set ;
4041IntegerSet * empty_leaf_set ;
4142MemoryContext page_set_context ;
42- }GistBulkDeleteResult ;
43-
44- /* Working state needed by gistbulkdelete */
45- typedef struct
46- {
47- IndexVacuumInfo * info ;
48- GistBulkDeleteResult * stats ;
49- IndexBulkDeleteCallback callback ;
50- void * callback_state ;
51- GistNSN startNSN ;
5243}GistVacState ;
5344
54- static void gistvacuumscan (IndexVacuumInfo * info ,GistBulkDeleteResult * stats ,
45+ static void gistvacuumscan (IndexVacuumInfo * info ,IndexBulkDeleteResult * stats ,
5546IndexBulkDeleteCallback callback ,void * callback_state );
5647static void gistvacuumpage (GistVacState * vstate ,BlockNumber blkno ,
5748BlockNumber orig_blkno );
5849static void gistvacuum_delete_empty_pages (IndexVacuumInfo * info ,
59- GistBulkDeleteResult * stats );
60- static bool gistdeletepage (IndexVacuumInfo * info ,GistBulkDeleteResult * stats ,
50+ GistVacState * vstate );
51+ static bool gistdeletepage (IndexVacuumInfo * info ,IndexBulkDeleteResult * stats ,
6152Buffer buffer ,OffsetNumber downlink ,
6253Buffer leafBuffer );
6354
64- /* allocate the 'stats' struct that's kept over vacuum stages */
65- static GistBulkDeleteResult *
66- create_GistBulkDeleteResult (void )
67- {
68- GistBulkDeleteResult * gist_stats ;
69-
70- gist_stats = (GistBulkDeleteResult * )palloc0 (sizeof (GistBulkDeleteResult ));
71- gist_stats -> page_set_context =
72- GenerationContextCreate (CurrentMemoryContext ,
73- "GiST VACUUM page set context" ,
74- 16 * 1024 );
75-
76- return gist_stats ;
77- }
78-
7955/*
8056 * VACUUM bulkdelete stage: remove index entries.
8157 */
8258IndexBulkDeleteResult *
8359gistbulkdelete (IndexVacuumInfo * info ,IndexBulkDeleteResult * stats ,
8460IndexBulkDeleteCallback callback ,void * callback_state )
8561{
86- GistBulkDeleteResult * gist_stats = (GistBulkDeleteResult * )stats ;
87-
8862/* allocate stats if first time through, else re-use existing struct */
89- if (gist_stats == NULL )
90- gist_stats = create_GistBulkDeleteResult ( );
63+ if (stats == NULL )
64+ stats = ( IndexBulkDeleteResult * ) palloc0 ( sizeof ( IndexBulkDeleteResult ) );
9165
92- gistvacuumscan (info ,gist_stats ,callback ,callback_state );
66+ gistvacuumscan (info ,stats ,callback ,callback_state );
9367
94- return ( IndexBulkDeleteResult * ) gist_stats ;
68+ return stats ;
9569}
9670
9771/*
@@ -100,8 +74,6 @@ gistbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
10074IndexBulkDeleteResult *
10175gistvacuumcleanup (IndexVacuumInfo * info ,IndexBulkDeleteResult * stats )
10276{
103- GistBulkDeleteResult * gist_stats = (GistBulkDeleteResult * )stats ;
104-
10577/* No-op in ANALYZE ONLY mode */
10678if (info -> analyze_only )
10779return stats ;
@@ -111,24 +83,12 @@ gistvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats)
11183 * stats from the latest gistbulkdelete call. If it wasn't called, we
11284 * still need to do a pass over the index, to obtain index statistics.
11385 */
114- if (gist_stats == NULL )
86+ if (stats == NULL )
11587{
116- gist_stats = create_GistBulkDeleteResult ( );
117- gistvacuumscan (info ,gist_stats ,NULL ,NULL );
88+ stats = ( IndexBulkDeleteResult * ) palloc0 ( sizeof ( IndexBulkDeleteResult ) );
89+ gistvacuumscan (info ,stats ,NULL ,NULL );
11890}
11991
120- /*
121- * If we saw any empty pages, try to unlink them from the tree so that
122- * they can be reused.
123- */
124- gistvacuum_delete_empty_pages (info ,gist_stats );
125-
126- /* we don't need the internal and empty page sets anymore */
127- MemoryContextDelete (gist_stats -> page_set_context );
128- gist_stats -> page_set_context = NULL ;
129- gist_stats -> internal_page_set = NULL ;
130- gist_stats -> empty_leaf_set = NULL ;
131-
13292/*
13393 * It's quite possible for us to be fooled by concurrent page splits into
13494 * double-counting some index tuples, so disbelieve any total that exceeds
@@ -137,11 +97,11 @@ gistvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats)
13797 */
13898if (!info -> estimated_count )
13999{
140- if (gist_stats -> stats . num_index_tuples > info -> num_heap_tuples )
141- gist_stats -> stats . num_index_tuples = info -> num_heap_tuples ;
100+ if (stats -> num_index_tuples > info -> num_heap_tuples )
101+ stats -> num_index_tuples = info -> num_heap_tuples ;
142102}
143103
144- return ( IndexBulkDeleteResult * ) gist_stats ;
104+ return stats ;
145105}
146106
147107/*
@@ -153,15 +113,16 @@ gistvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats)
153113 * occurred).
154114 *
155115 * This also makes note of any empty leaf pages, as well as all internal
156- * pages. The second stage, gistvacuum_delete_empty_pages(), needs that
157- * information. Any deleted pages are added directly to the free space map.
158- * (They should've been added there when they were originally deleted, already,
159- * but it's possible that the FSM was lost at a crash, for example.)
116+ * pages while looping over all index pages. After scanning all the pages, we
117+ * remove the empty pages so that they can be reused. Any deleted pages are
118+ * added directly to the free space map. (They should've been added there
119+ * when they were originally deleted, already, but it's possible that the FSM
120+ * was lost at a crash, for example.)
160121 *
161122 * The caller is responsible for initially allocating/zeroing a stats struct.
162123 */
163124static void
164- gistvacuumscan (IndexVacuumInfo * info ,GistBulkDeleteResult * stats ,
125+ gistvacuumscan (IndexVacuumInfo * info ,IndexBulkDeleteResult * stats ,
165126IndexBulkDeleteCallback callback ,void * callback_state )
166127{
167128Relation rel = info -> index ;
@@ -175,21 +136,23 @@ gistvacuumscan(IndexVacuumInfo *info, GistBulkDeleteResult *stats,
175136 * Reset counts that will be incremented during the scan; needed in case
176137 * of multiple scans during a single VACUUM command.
177138 */
178- stats -> stats .estimated_count = false;
179- stats -> stats .num_index_tuples = 0 ;
180- stats -> stats .pages_deleted = 0 ;
181- stats -> stats .pages_free = 0 ;
182- MemoryContextReset (stats -> page_set_context );
139+ stats -> estimated_count = false;
140+ stats -> num_index_tuples = 0 ;
141+ stats -> pages_deleted = 0 ;
142+ stats -> pages_free = 0 ;
183143
184144/*
185145 * Create the integer sets to remember all the internal and the empty leaf
186146 * pages in page_set_context. Internally, the integer set will remember
187147 * this context so that the subsequent allocations for these integer sets
188148 * will be done from the same context.
189149 */
190- oldctx = MemoryContextSwitchTo (stats -> page_set_context );
191- stats -> internal_page_set = intset_create ();
192- stats -> empty_leaf_set = intset_create ();
150+ vstate .page_set_context = GenerationContextCreate (CurrentMemoryContext ,
151+ "GiST VACUUM page set context" ,
152+ 16 * 1024 );
153+ oldctx = MemoryContextSwitchTo (vstate .page_set_context );
154+ vstate .internal_page_set = intset_create ();
155+ vstate .empty_leaf_set = intset_create ();
193156MemoryContextSwitchTo (oldctx );
194157
195158/* Set up info to pass down to gistvacuumpage */
@@ -257,11 +220,23 @@ gistvacuumscan(IndexVacuumInfo *info, GistBulkDeleteResult *stats,
257220 * Note that if no recyclable pages exist, we don't bother vacuuming the
258221 * FSM at all.
259222 */
260- if (stats -> stats . pages_free > 0 )
223+ if (stats -> pages_free > 0 )
261224IndexFreeSpaceMapVacuum (rel );
262225
263226/* update statistics */
264- stats -> stats .num_pages = num_pages ;
227+ stats -> num_pages = num_pages ;
228+
229+ /*
230+ * If we saw any empty pages, try to unlink them from the tree so that
231+ * they can be reused.
232+ */
233+ gistvacuum_delete_empty_pages (info ,& vstate );
234+
235+ /* we don't need the internal and empty page sets anymore */
236+ MemoryContextDelete (vstate .page_set_context );
237+ vstate .page_set_context = NULL ;
238+ vstate .internal_page_set = NULL ;
239+ vstate .empty_leaf_set = NULL ;
265240}
266241
267242/*
@@ -278,7 +253,6 @@ gistvacuumscan(IndexVacuumInfo *info, GistBulkDeleteResult *stats,
278253static void
279254gistvacuumpage (GistVacState * vstate ,BlockNumber blkno ,BlockNumber orig_blkno )
280255{
281- GistBulkDeleteResult * stats = vstate -> stats ;
282256IndexVacuumInfo * info = vstate -> info ;
283257IndexBulkDeleteCallback callback = vstate -> callback ;
284258void * callback_state = vstate -> callback_state ;
@@ -307,13 +281,13 @@ gistvacuumpage(GistVacState *vstate, BlockNumber blkno, BlockNumber orig_blkno)
307281{
308282/* Okay to recycle this page */
309283RecordFreeIndexPage (rel ,blkno );
310- stats -> stats . pages_free ++ ;
311- stats -> stats . pages_deleted ++ ;
284+ vstate -> stats -> pages_free ++ ;
285+ vstate -> stats -> pages_deleted ++ ;
312286}
313287else if (GistPageIsDeleted (page ))
314288{
315289/* Already deleted, but can't recycle yet */
316- stats -> stats . pages_deleted ++ ;
290+ vstate -> stats -> pages_deleted ++ ;
317291}
318292else if (GistPageIsLeaf (page ))
319293{
@@ -388,7 +362,7 @@ gistvacuumpage(GistVacState *vstate, BlockNumber blkno, BlockNumber orig_blkno)
388362
389363END_CRIT_SECTION ();
390364
391- stats -> stats . tuples_removed += ntodelete ;
365+ vstate -> stats -> tuples_removed += ntodelete ;
392366/* must recompute maxoff */
393367maxoff = PageGetMaxOffsetNumber (page );
394368}
@@ -405,10 +379,10 @@ gistvacuumpage(GistVacState *vstate, BlockNumber blkno, BlockNumber orig_blkno)
405379 * it up.
406380 */
407381if (blkno == orig_blkno )
408- intset_add_member (stats -> empty_leaf_set ,blkno );
382+ intset_add_member (vstate -> empty_leaf_set ,blkno );
409383}
410384else
411- stats -> stats . num_index_tuples += nremain ;
385+ vstate -> stats -> num_index_tuples += nremain ;
412386}
413387else
414388{
@@ -443,7 +417,7 @@ gistvacuumpage(GistVacState *vstate, BlockNumber blkno, BlockNumber orig_blkno)
443417 * parents of empty leaf pages.
444418 */
445419if (blkno == orig_blkno )
446- intset_add_member (stats -> internal_page_set ,blkno );
420+ intset_add_member (vstate -> internal_page_set ,blkno );
447421}
448422
449423UnlockReleaseBuffer (buffer );
@@ -466,7 +440,7 @@ gistvacuumpage(GistVacState *vstate, BlockNumber blkno, BlockNumber orig_blkno)
466440 * Scan all internal pages, and try to delete their empty child pages.
467441 */
468442static void
469- gistvacuum_delete_empty_pages (IndexVacuumInfo * info ,GistBulkDeleteResult * stats )
443+ gistvacuum_delete_empty_pages (IndexVacuumInfo * info ,GistVacState * vstate )
470444{
471445Relation rel = info -> index ;
472446BlockNumber empty_pages_remaining ;
@@ -475,10 +449,10 @@ gistvacuum_delete_empty_pages(IndexVacuumInfo *info, GistBulkDeleteResult *stats
475449/*
476450 * Rescan all inner pages to find those that have empty child pages.
477451 */
478- empty_pages_remaining = intset_num_entries (stats -> empty_leaf_set );
479- intset_begin_iterate (stats -> internal_page_set );
452+ empty_pages_remaining = intset_num_entries (vstate -> empty_leaf_set );
453+ intset_begin_iterate (vstate -> internal_page_set );
480454while (empty_pages_remaining > 0 &&
481- intset_iterate_next (stats -> internal_page_set ,& blkno ))
455+ intset_iterate_next (vstate -> internal_page_set ,& blkno ))
482456{
483457Buffer buffer ;
484458Page page ;
@@ -521,7 +495,7 @@ gistvacuum_delete_empty_pages(IndexVacuumInfo *info, GistBulkDeleteResult *stats
521495BlockNumber leafblk ;
522496
523497leafblk = ItemPointerGetBlockNumber (& (idxtuple -> t_tid ));
524- if (intset_is_member (stats -> empty_leaf_set ,leafblk ))
498+ if (intset_is_member (vstate -> empty_leaf_set ,leafblk ))
525499{
526500leafs_to_delete [ntodelete ]= leafblk ;
527501todelete [ntodelete ++ ]= off ;
@@ -561,7 +535,7 @@ gistvacuum_delete_empty_pages(IndexVacuumInfo *info, GistBulkDeleteResult *stats
561535gistcheckpage (rel ,leafbuf );
562536
563537LockBuffer (buffer ,GIST_EXCLUSIVE );
564- if (gistdeletepage (info ,stats ,
538+ if (gistdeletepage (info ,vstate -> stats ,
565539buffer ,todelete [i ]- deleted ,
566540leafbuf ))
567541deleted ++ ;
@@ -573,7 +547,7 @@ gistvacuum_delete_empty_pages(IndexVacuumInfo *info, GistBulkDeleteResult *stats
573547ReleaseBuffer (buffer );
574548
575549/* update stats */
576- stats -> stats . pages_removed += deleted ;
550+ vstate -> stats -> pages_removed += deleted ;
577551
578552/*
579553 * We can stop the scan as soon as we have seen the downlinks, even if
@@ -596,7 +570,7 @@ gistvacuum_delete_empty_pages(IndexVacuumInfo *info, GistBulkDeleteResult *stats
596570 * prevented it.
597571 */
598572static bool
599- gistdeletepage (IndexVacuumInfo * info ,GistBulkDeleteResult * stats ,
573+ gistdeletepage (IndexVacuumInfo * info ,IndexBulkDeleteResult * stats ,
600574Buffer parentBuffer ,OffsetNumber downlink ,
601575Buffer leafBuffer )
602576{
@@ -665,7 +639,7 @@ gistdeletepage(IndexVacuumInfo *info, GistBulkDeleteResult *stats,
665639/* mark the page as deleted */
666640MarkBufferDirty (leafBuffer );
667641GistPageSetDeleted (leafPage ,txid );
668- stats -> stats . pages_deleted ++ ;
642+ stats -> pages_deleted ++ ;
669643
670644/* remove the downlink from the parent */
671645MarkBufferDirty (parentBuffer );