112112
113113typedef struct LVRelStats
114114{
115- /*hasindex = true means two-pass strategy; false means one-pass */
116- bool hasindex ;
115+ /*useindex = true means two-pass strategy; false means one-pass */
116+ bool useindex ;
117117/* Overall statistics about rel */
118118BlockNumber old_rel_pages ;/* previous value of pg_class.relpages */
119119BlockNumber rel_pages ;/* total number of pages */
@@ -125,6 +125,8 @@ typedef struct LVRelStats
125125double new_rel_tuples ;/* new estimated total # of tuples */
126126double new_live_tuples ;/* new estimated total # of live tuples */
127127double new_dead_tuples ;/* new estimated total # of dead tuples */
128+ double nleft_dead_tuples ;/* # of dead tuples we left */
129+ double nleft_dead_itemids ;/* # of dead item pointers we left */
128130BlockNumber pages_removed ;
129131double tuples_deleted ;
130132BlockNumber nonempty_pages ;/* actually, last nonempty page + 1 */
@@ -150,7 +152,7 @@ static BufferAccessStrategy vac_strategy;
150152
151153
152154/* non-export function prototypes */
153- static void lazy_scan_heap (Relation onerel ,int options ,
155+ static void lazy_scan_heap (Relation onerel ,VacuumParams * params ,
154156LVRelStats * vacrelstats ,Relation * Irel ,int nindexes ,
155157bool aggressive );
156158static void lazy_vacuum_heap (Relation onerel ,LVRelStats * vacrelstats ,BlockNumber nblocks );
@@ -209,6 +211,7 @@ heap_vacuum_rel(Relation onerel, VacuumParams *params,
209211MultiXactId new_min_multi ;
210212
211213Assert (params != NULL );
214+ Assert (params -> index_cleanup != VACOPT_TERNARY_DEFAULT );
212215
213216/* measure elapsed time iff autovacuum logging requires it */
214217if (IsAutoVacuumWorkerProcess ()&& params -> log_min_duration >=0 )
@@ -275,10 +278,11 @@ heap_vacuum_rel(Relation onerel, VacuumParams *params,
275278
276279/* Open all indexes of the relation */
277280vac_open_indexes (onerel ,RowExclusiveLock ,& nindexes ,& Irel );
278- vacrelstats -> hasindex = (nindexes > 0 );
281+ vacrelstats -> useindex = (nindexes > 0 &&
282+ params -> index_cleanup == VACOPT_TERNARY_ENABLED );
279283
280284/* Do the vacuuming */
281- lazy_scan_heap (onerel ,params -> options ,vacrelstats ,Irel ,nindexes ,aggressive );
285+ lazy_scan_heap (onerel ,params ,vacrelstats ,Irel ,nindexes ,aggressive );
282286
283287/* Done with indexes */
284288vac_close_indexes (nindexes ,Irel ,NoLock );
@@ -349,7 +353,7 @@ heap_vacuum_rel(Relation onerel, VacuumParams *params,
349353new_rel_pages ,
350354new_live_tuples ,
351355new_rel_allvisible ,
352- vacrelstats -> hasindex ,
356+ nindexes > 0 ,
353357new_frozen_xid ,
354358new_min_multi ,
355359false);
@@ -419,6 +423,12 @@ heap_vacuum_rel(Relation onerel, VacuumParams *params,
419423vacrelstats -> new_rel_tuples ,
420424vacrelstats -> new_dead_tuples ,
421425OldestXmin );
426+ if (vacrelstats -> nleft_dead_tuples > 0 ||
427+ vacrelstats -> nleft_dead_itemids > 0 )
428+ appendStringInfo (& buf ,
429+ _ ("%.0f tuples and %.0f item identifiers are left as dead.\n" ),
430+ vacrelstats -> nleft_dead_tuples ,
431+ vacrelstats -> nleft_dead_itemids );
422432appendStringInfo (& buf ,
423433_ ("buffer usage: %d hits, %d misses, %d dirtied\n" ),
424434VacuumPageHit ,
@@ -485,7 +495,7 @@ vacuum_log_cleanup_info(Relation rel, LVRelStats *vacrelstats)
485495 *reference them have been killed.
486496 */
487497static void
488- lazy_scan_heap (Relation onerel ,int options ,LVRelStats * vacrelstats ,
498+ lazy_scan_heap (Relation onerel ,VacuumParams * params ,LVRelStats * vacrelstats ,
489499Relation * Irel ,int nindexes ,bool aggressive )
490500{
491501BlockNumber nblocks ,
@@ -501,7 +511,10 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats,
501511live_tuples ,/* live tuples (reltuples estimate) */
502512tups_vacuumed ,/* tuples cleaned up by vacuum */
503513nkeep ,/* dead-but-not-removable tuples */
504- nunused ;/* unused item pointers */
514+ nunused ,/* unused item pointers */
515+ nleft_dead_tuples ,/* tuples we left as dead */
516+ nleft_dead_itemids ;/* item pointers we left as dead,
517+ * includes nleft_dead_tuples. */
505518IndexBulkDeleteResult * * indstats ;
506519int i ;
507520PGRUsage ru0 ;
@@ -534,6 +547,7 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats,
534547empty_pages = vacuumed_pages = 0 ;
535548next_fsm_block_to_vacuum = (BlockNumber )0 ;
536549num_tuples = live_tuples = tups_vacuumed = nkeep = nunused = 0 ;
550+ nleft_dead_itemids = nleft_dead_tuples = 0 ;
537551
538552indstats = (IndexBulkDeleteResult * * )
539553palloc0 (nindexes * sizeof (IndexBulkDeleteResult * ));
@@ -599,7 +613,7 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats,
599613 * be replayed on any hot standby, where it can be disruptive.
600614 */
601615next_unskippable_block = 0 ;
602- if ((options & VACOPT_DISABLE_PAGE_SKIPPING )== 0 )
616+ if ((params -> options & VACOPT_DISABLE_PAGE_SKIPPING )== 0 )
603617{
604618while (next_unskippable_block < nblocks )
605619{
@@ -654,7 +668,7 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats,
654668{
655669/* Time to advance next_unskippable_block */
656670next_unskippable_block ++ ;
657- if ((options & VACOPT_DISABLE_PAGE_SKIPPING )== 0 )
671+ if ((params -> options & VACOPT_DISABLE_PAGE_SKIPPING )== 0 )
658672{
659673while (next_unskippable_block < nblocks )
660674{
@@ -1070,7 +1084,17 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats,
10701084HeapTupleIsHeapOnly (& tuple ))
10711085nkeep += 1 ;
10721086else
1087+ {
10731088tupgone = true;/* we can delete the tuple */
1089+
1090+ /*
1091+ * Since this dead tuple will not be vacuumed and
1092+ * ignored when index cleanup is disabled we count
1093+ * count it for reporting.
1094+ */
1095+ if (params -> index_cleanup == VACOPT_TERNARY_ENABLED )
1096+ nleft_dead_tuples ++ ;
1097+ }
10741098all_visible = false;
10751099break ;
10761100case HEAPTUPLE_LIVE :
@@ -1222,23 +1246,40 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats,
12221246}
12231247
12241248/*
1225- * If there are no indexes then we can vacuum the page right now
1226- * instead of doing a second scan.
1249+ * If there are no indexes we can vacuum the page right now instead of
1250+ * doing a second scan. Also we don't do that but forget dead tuples
1251+ * when index cleanup is disabled.
12271252 */
1228- if (nindexes == 0 &&
1229- vacrelstats -> num_dead_tuples > 0 )
1253+ if (!vacrelstats -> useindex && vacrelstats -> num_dead_tuples > 0 )
12301254{
1231- /* Remove tuples from heap */
1232- lazy_vacuum_page (onerel ,blkno ,buf ,0 ,vacrelstats ,& vmbuffer );
1233- has_dead_tuples = false;
1255+ if (nindexes == 0 )
1256+ {
1257+ /* Remove tuples from heap if the table has no index */
1258+ lazy_vacuum_page (onerel ,blkno ,buf ,0 ,vacrelstats ,& vmbuffer );
1259+ vacuumed_pages ++ ;
1260+ has_dead_tuples = false;
1261+ }
1262+ else
1263+ {
1264+ /*
1265+ * Here, we have indexes but index cleanup is disabled. Instead of
1266+ * vacuuming the dead tuples on the heap, we just forget them.
1267+ *
1268+ * Note that vacrelstats->dead_tuples could have tuples which
1269+ * became dead after HOT-pruning but are not marked dead yet.
1270+ * We do not process them because it's a very rare condition, and
1271+ * the next vacuum will process them anyway.
1272+ */
1273+ Assert (params -> index_cleanup == VACOPT_TERNARY_DISABLED );
1274+ nleft_dead_itemids += vacrelstats -> num_dead_tuples ;
1275+ }
12341276
12351277/*
12361278 * Forget the now-vacuumed tuples, and press on, but be careful
12371279 * not to reset latestRemovedXid since we want that value to be
12381280 * valid.
12391281 */
12401282vacrelstats -> num_dead_tuples = 0 ;
1241- vacuumed_pages ++ ;
12421283
12431284/*
12441285 * Periodically do incremental FSM vacuuming to make newly-freed
@@ -1357,14 +1398,21 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats,
13571398RecordPageWithFreeSpace (onerel ,blkno ,freespace ,nblocks );
13581399}
13591400
1401+ /* No dead tuples should be left if index cleanup is enabled */
1402+ Assert ((params -> index_cleanup == VACOPT_TERNARY_ENABLED &&
1403+ nleft_dead_tuples == 0 && nleft_dead_itemids == 0 )||
1404+ params -> index_cleanup == VACOPT_TERNARY_DISABLED );
1405+
13601406/* report that everything is scanned and vacuumed */
13611407pgstat_progress_update_param (PROGRESS_VACUUM_HEAP_BLKS_SCANNED ,blkno );
13621408
13631409pfree (frozen );
13641410
13651411/* save stats for use later */
13661412vacrelstats -> tuples_deleted = tups_vacuumed ;
1367- vacrelstats -> new_dead_tuples = nkeep ;
1413+ vacrelstats -> new_dead_tuples = nkeep + nleft_dead_tuples ;
1414+ vacrelstats -> nleft_dead_tuples = nleft_dead_tuples ;
1415+ vacrelstats -> nleft_dead_itemids = nleft_dead_itemids ;
13681416
13691417/* now we can compute the new value for pg_class.reltuples */
13701418vacrelstats -> new_live_tuples = vac_estimate_reltuples (onerel ,
@@ -1433,8 +1481,11 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats,
14331481PROGRESS_VACUUM_PHASE_INDEX_CLEANUP );
14341482
14351483/* Do post-vacuum cleanup and statistics update for each index */
1436- for (i = 0 ;i < nindexes ;i ++ )
1437- lazy_cleanup_index (Irel [i ],indstats [i ],vacrelstats );
1484+ if (vacrelstats -> useindex )
1485+ {
1486+ for (i = 0 ;i < nindexes ;i ++ )
1487+ lazy_cleanup_index (Irel [i ],indstats [i ],vacrelstats );
1488+ }
14381489
14391490/* If no indexes, make log report that lazy_vacuum_heap would've made */
14401491if (vacuumed_pages )
@@ -1465,6 +1516,8 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats,
14651516"%u pages are entirely empty.\n" ,
14661517empty_pages ),
14671518empty_pages );
1519+ appendStringInfo (& buf ,"%.0f tuples and %.0f item identifiers are left as dead.\n" ,
1520+ nleft_dead_tuples ,nleft_dead_itemids );
14681521appendStringInfo (& buf ,_ ("%s." ),pg_rusage_show (& ru0 ));
14691522
14701523ereport (elevel ,
@@ -2110,7 +2163,7 @@ lazy_space_alloc(LVRelStats *vacrelstats, BlockNumber relblocks)
21102163autovacuum_work_mem != -1 ?
21112164autovacuum_work_mem :maintenance_work_mem ;
21122165
2113- if (vacrelstats -> hasindex )
2166+ if (vacrelstats -> useindex )
21142167{
21152168maxtuples = (vac_work_mem * 1024L ) /sizeof (ItemPointerData );
21162169maxtuples = Min (maxtuples ,INT_MAX );