1616 * perform a pass of index cleanup and page compaction, then resume the heap
1717 * scan with an empty TID array.
1818 *
19- * As a special exception if we're processing a table with no indexes we can
20- * vacuum each page as we go so we don't need to allocate more space than
21- * enough to hold as many heap tuples fit on one page.
22- *
2319 * We can limit the storage for page free space to MaxFSMPages entries,
2420 * since that's the most the free space map will be willing to remember
2521 * anyway.If the relation has fewer than that many pages with free space,
2925 * have more than MaxFSMPages entries in all. The surviving page entries
3026 * are passed to the free space map at conclusion of the scan.
3127 *
28+ * If we're processing a table with no indexes, we can just vacuum each page
29+ * as we go; there's no need to save up multiple tuples to minimize the number
30+ * of index scans performed. So we don't use maintenance_work_mem memory for
31+ * the TID array, just enough to hold as many heap tuples as fit on one page.
32+ *
3233 *
3334 * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
3435 * Portions Copyright (c) 1994, Regents of the University of California
3536 *
3637 *
3738 * IDENTIFICATION
38- * $PostgreSQL: pgsql/src/backend/commands/vacuumlazy.c,v 1.77 2006/09/04 21:40:23 momjian Exp $
39+ * $PostgreSQL: pgsql/src/backend/commands/vacuumlazy.c,v 1.78 2006/09/13 17:47:08 tgl Exp $
3940 *
4041 *-------------------------------------------------------------------------
4142 */
6869
6970typedef struct LVRelStats
7071{
72+ /* hasindex = true means two-pass strategy; false means one-pass */
73+ bool hasindex ;
7174/* Overall statistics about rel */
7275BlockNumber rel_pages ;
7376double rel_tuples ;
@@ -110,7 +113,7 @@ static void lazy_truncate_heap(Relation onerel, LVRelStats *vacrelstats,
110113TransactionId OldestXmin );
111114static BlockNumber count_nondeletable_pages (Relation onerel ,
112115LVRelStats * vacrelstats ,TransactionId OldestXmin );
113- static void lazy_space_alloc (LVRelStats * vacrelstats ,BlockNumber relblocks , unsigned nindexes );
116+ static void lazy_space_alloc (LVRelStats * vacrelstats ,BlockNumber relblocks );
114117static void lazy_record_dead_tuple (LVRelStats * vacrelstats ,
115118ItemPointer itemptr );
116119static void lazy_record_free_space (LVRelStats * vacrelstats ,
@@ -137,7 +140,6 @@ lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt)
137140LVRelStats * vacrelstats ;
138141Relation * Irel ;
139142int nindexes ;
140- bool hasindex ;
141143BlockNumber possibly_freeable ;
142144TransactionId OldestXmin ,
143145FreezeLimit ;
@@ -169,7 +171,7 @@ lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt)
169171
170172/* Open all indexes of the relation */
171173vac_open_indexes (onerel ,RowExclusiveLock ,& nindexes ,& Irel );
172- hasindex = (nindexes > 0 );
174+ vacrelstats -> hasindex = (nindexes > 0 );
173175
174176/* Do the vacuuming */
175177lazy_scan_heap (onerel ,vacrelstats ,Irel ,nindexes ,FreezeLimit ,OldestXmin );
@@ -195,7 +197,7 @@ lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt)
195197vac_update_relstats (RelationGetRelid (onerel ),
196198vacrelstats -> rel_pages ,
197199vacrelstats -> rel_tuples ,
198- hasindex ,
200+ vacrelstats -> hasindex ,
199201vacrelstats -> minxid ,OldestXmin );
200202
201203/* report results to the stats collector, too */
@@ -210,11 +212,13 @@ lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt)
210212 *This routine sets commit status bits, builds lists of dead tuples
211213 *and pages with free space, and calculates statistics on the number
212214 *of live tuples in the heap. When done, or when we run low on space
213- *for dead-tuple TIDs, or after every page if the table has no indexes
214- *invoke vacuuming of indexes and heap.
215+ *for dead-tuple TIDs, invoke vacuuming of indexes and heap.
215216 *
216217 *It also updates the minimum Xid found anywhere on the table in
217218 *vacrelstats->minxid, for later storing it in pg_class.relminxid.
219+ *
220+ *If there are no indexes then we just vacuum each dirty page as we
221+ *process it, since there's no point in gathering many tuples.
218222 */
219223static void
220224lazy_scan_heap (Relation onerel ,LVRelStats * vacrelstats ,
@@ -225,7 +229,8 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
225229blkno ;
226230HeapTupleData tuple ;
227231char * relname ;
228- BlockNumber empty_pages ;
232+ BlockNumber empty_pages ,
233+ vacuumed_pages ;
229234double num_tuples ,
230235tups_vacuumed ,
231236nkeep ,
@@ -242,7 +247,7 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
242247get_namespace_name (RelationGetNamespace (onerel )),
243248relname )));
244249
245- empty_pages = 0 ;
250+ empty_pages = vacuumed_pages = 0 ;
246251num_tuples = tups_vacuumed = nkeep = nunused = 0 ;
247252
248253indstats = (IndexBulkDeleteResult * * )
@@ -252,7 +257,7 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
252257vacrelstats -> rel_pages = nblocks ;
253258vacrelstats -> nonempty_pages = 0 ;
254259
255- lazy_space_alloc (vacrelstats ,nblocks , nindexes );
260+ lazy_space_alloc (vacrelstats ,nblocks );
256261
257262for (blkno = 0 ;blkno < nblocks ;blkno ++ )
258263{
@@ -287,14 +292,8 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
287292
288293buf = ReadBuffer (onerel ,blkno );
289294
290- /* In this phase we only need shared access to the buffer unless we're
291- * going to do the vacuuming now which we do if there are no indexes
292- */
293-
294- if (nindexes )
295- LockBuffer (buf ,BUFFER_LOCK_SHARE );
296- else
297- LockBufferForCleanup (buf );
295+ /* In this phase we only need shared access to the buffer */
296+ LockBuffer (buf ,BUFFER_LOCK_SHARE );
298297
299298page = BufferGetPage (buf );
300299
@@ -451,22 +450,34 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
451450}
452451}/* scan along page */
453452
453+ /*
454+ * If there are no indexes then we can vacuum the page right now
455+ * instead of doing a second scan.
456+ */
457+ if (nindexes == 0 &&
458+ vacrelstats -> num_dead_tuples > 0 )
459+ {
460+ /* Trade in buffer share lock for super-exclusive lock */
461+ LockBuffer (buf ,BUFFER_LOCK_UNLOCK );
462+ LockBufferForCleanup (buf );
463+ /* Remove tuples from heap */
464+ lazy_vacuum_page (onerel ,blkno ,buf ,0 ,vacrelstats );
465+ /* Forget the now-vacuumed tuples, and press on */
466+ vacrelstats -> num_dead_tuples = 0 ;
467+ vacuumed_pages ++ ;
468+ }
469+
454470/*
455471 * If we remembered any tuples for deletion, then the page will be
456472 * visited again by lazy_vacuum_heap, which will compute and record
457473 * its post-compaction free space.If not, then we're done with this
458- * page, so remember its free space as-is.
474+ * page, so remember its free space as-is. (This path will always
475+ * be taken if there are no indexes.)
459476 */
460477if (vacrelstats -> num_dead_tuples == prev_dead_count )
461478{
462479lazy_record_free_space (vacrelstats ,blkno ,
463480PageGetFreeSpace (page ));
464- }else if (!nindexes ) {
465- /* If there are no indexes we can vacuum the page right now instead
466- * of doing a second scan */
467- lazy_vacuum_page (onerel ,blkno ,buf ,0 ,vacrelstats );
468- lazy_record_free_space (vacrelstats ,blkno ,PageGetFreeSpace (BufferGetPage (buf )));
469- vacrelstats -> num_dead_tuples = 0 ;
470481}
471482
472483/* Remember the location of the last page with nonremovable tuples */
@@ -499,6 +510,13 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
499510for (i = 0 ;i < nindexes ;i ++ )
500511lazy_cleanup_index (Irel [i ],indstats [i ],vacrelstats );
501512
513+ /* If no indexes, make log report that lazy_vacuum_heap would've made */
514+ if (vacuumed_pages )
515+ ereport (elevel ,
516+ (errmsg ("\"%s\": removed %.0f row versions in %u pages" ,
517+ RelationGetRelationName (onerel ),
518+ tups_vacuumed ,vacuumed_pages )));
519+
502520ereport (elevel ,
503521(errmsg ("\"%s\": found %.0f removable, %.0f nonremovable row versions in %u pages" ,
504522RelationGetRelationName (onerel ),
@@ -908,18 +926,21 @@ count_nondeletable_pages(Relation onerel, LVRelStats *vacrelstats,
908926 * See the comments at the head of this file for rationale.
909927 */
910928static void
911- lazy_space_alloc (LVRelStats * vacrelstats ,BlockNumber relblocks , unsigned nindexes )
929+ lazy_space_alloc (LVRelStats * vacrelstats ,BlockNumber relblocks )
912930{
913931long maxtuples ;
914932int maxpages ;
915933
916- if (nindexes ) {
934+ if (vacrelstats -> hasindex )
935+ {
917936maxtuples = (maintenance_work_mem * 1024L ) /sizeof (ItemPointerData );
918937maxtuples = Min (maxtuples ,INT_MAX );
919938maxtuples = Min (maxtuples ,MaxAllocSize /sizeof (ItemPointerData ));
920939/* stay sane if small maintenance_work_mem */
921940maxtuples = Max (maxtuples ,MaxHeapTuplesPerPage );
922- }else {
941+ }
942+ else
943+ {
923944maxtuples = MaxHeapTuplesPerPage ;
924945}
925946