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 */
118118 BlockNumber old_rel_pages ; /* previous value of pg_class.relpages */
119119 BlockNumber rel_pages ; /* total number of pages */
@@ -125,6 +125,8 @@ typedef struct LVRelStats
125125 double new_rel_tuples ; /* new estimated total # of tuples */
126126 double new_live_tuples ; /* new estimated total # of live tuples */
127127 double 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 */
128130 BlockNumber pages_removed ;
129131 double tuples_deleted ;
130132 BlockNumber 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 ,
154156 LVRelStats * vacrelstats , Relation * Irel , int nindexes ,
155157 bool aggressive );
156158static void lazy_vacuum_heap (Relation onerel , LVRelStats * vacrelstats , BlockNumber nblocks );
@@ -209,6 +211,7 @@ heap_vacuum_rel(Relation onerel, VacuumParams *params,
209211 MultiXactId new_min_multi ;
210212
211213 Assert (params != NULL );
214+ Assert (params -> index_cleanup != VACOPT_TERNARY_DEFAULT );
212215
213216 /* measure elapsed time iff autovacuum logging requires it */
214217 if (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 */
277280 vac_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 */
284288 vac_close_indexes (nindexes , Irel , NoLock );
@@ -349,7 +353,7 @@ heap_vacuum_rel(Relation onerel, VacuumParams *params,
349353 new_rel_pages ,
350354 new_live_tuples ,
351355 new_rel_allvisible ,
352- vacrelstats -> hasindex ,
356+ nindexes > 0 ,
353357 new_frozen_xid ,
354358 new_min_multi ,
355359 false);
@@ -419,6 +423,12 @@ heap_vacuum_rel(Relation onerel, VacuumParams *params,
419423 vacrelstats -> new_rel_tuples ,
420424 vacrelstats -> new_dead_tuples ,
421425 OldestXmin );
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 );
422432 appendStringInfo (& buf ,
423433 _ ("buffer usage: %d hits, %d misses, %d dirtied\n" ),
424434 VacuumPageHit ,
@@ -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 ,
489499 Relation * Irel , int nindexes , bool aggressive )
490500{
491501 BlockNumber nblocks ,
@@ -501,7 +511,10 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats,
501511 live_tuples , /* live tuples (reltuples estimate) */
502512 tups_vacuumed , /* tuples cleaned up by vacuum */
503513 nkeep , /* 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. */
505518 IndexBulkDeleteResult * * indstats ;
506519 int i ;
507520 PGRUsage ru0 ;
@@ -534,6 +547,7 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats,
534547 empty_pages = vacuumed_pages = 0 ;
535548 next_fsm_block_to_vacuum = (BlockNumber ) 0 ;
536549 num_tuples = live_tuples = tups_vacuumed = nkeep = nunused = 0 ;
550+ nleft_dead_itemids = nleft_dead_tuples = 0 ;
537551
538552 indstats = (IndexBulkDeleteResult * * )
539553 palloc0 (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 */
601615 next_unskippable_block = 0 ;
602- if ((options & VACOPT_DISABLE_PAGE_SKIPPING ) == 0 )
616+ if ((params -> options & VACOPT_DISABLE_PAGE_SKIPPING ) == 0 )
603617 {
604618 while (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 */
656670 next_unskippable_block ++ ;
657- if ((options & VACOPT_DISABLE_PAGE_SKIPPING ) == 0 )
671+ if ((params -> options & VACOPT_DISABLE_PAGE_SKIPPING ) == 0 )
658672 {
659673 while (next_unskippable_block < nblocks )
660674 {
@@ -1070,7 +1084,17 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats,
10701084 HeapTupleIsHeapOnly (& tuple ))
10711085 nkeep += 1 ;
10721086 else
1087+ {
10731088 tupgone = 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+ }
10741098 all_visible = false;
10751099 break ;
10761100 case 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 */
12401282 vacrelstats -> 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,
13571398 RecordPageWithFreeSpace (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 */
13611407 pgstat_progress_update_param (PROGRESS_VACUUM_HEAP_BLKS_SCANNED , blkno );
13621408
13631409 pfree (frozen );
13641410
13651411 /* save stats for use later */
13661412 vacrelstats -> 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 */
13701418 vacrelstats -> new_live_tuples = vac_estimate_reltuples (onerel ,
@@ -1433,8 +1481,11 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats,
14331481 PROGRESS_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 */
14401491 if (vacuumed_pages )
@@ -1465,6 +1516,8 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats,
14651516 "%u pages are entirely empty.\n" ,
14661517 empty_pages ),
14671518 empty_pages );
1519+ appendStringInfo (& buf , "%.0f tuples and %.0f item identifiers are left as dead.\n" ,
1520+ nleft_dead_tuples , nleft_dead_itemids );
14681521 appendStringInfo (& buf , _ ("%s." ), pg_rusage_show (& ru0 ));
14691522
14701523 ereport (elevel ,
@@ -2110,7 +2163,7 @@ lazy_space_alloc(LVRelStats *vacrelstats, BlockNumber relblocks)
21102163 autovacuum_work_mem != -1 ?
21112164 autovacuum_work_mem : maintenance_work_mem ;
21122165
2113- if (vacrelstats -> hasindex )
2166+ if (vacrelstats -> useindex )
21142167 {
21152168 maxtuples = (vac_work_mem * 1024L ) / sizeof (ItemPointerData );
21162169 maxtuples = Min (maxtuples , INT_MAX );
0 commit comments