From ccf193f1a595106cacb946a41ef39e1cb2259e60 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Wed, 18 Jul 2001 00:46:25 +0000
Subject: [PATCH] New-style vacuum neglected to update pg_class statistics
 about indexes if there were no deletions to do.

---
 src/backend/commands/vacuum.c     | 69 ++++++++++++++----------
 src/backend/commands/vacuumlazy.c | 89 +++++++++++++++++++++++++++++--
 src/include/commands/vacuum.h     |  3 +-
 3 files changed, 128 insertions(+), 33 deletions(-)

diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index c53fa05812e..8e141133a42 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -13,7 +13,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.205 2001/07/15 22:48:17 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.206 2001/07/18 00:46:24 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -129,11 +129,11 @@ static void vacuum_index(VacPageList vacpagelist, Relation indrel,
 						 double num_tuples, int keep_tuples);
 static void scan_index(Relation indrel, double num_tuples);
 static bool tid_reaped(ItemPointer itemptr, void *state);
+static bool dummy_tid_reaped(ItemPointer itemptr, void *state);
 static void vac_update_fsm(Relation onerel, VacPageList fraged_pages,
 						   BlockNumber rel_pages);
 static VacPage copy_vac_page(VacPage vacpage);
 static void vpage_insert(VacPageList vacpagelist, VacPage vpnew);
-static bool is_partial_index(Relation indrel);
 static void *vac_bsearch(const void *key, const void *base,
 						 size_t nelem, size_t size,
 						 int (*compar) (const void *, const void *));
@@ -2178,51 +2178,52 @@ vacuum_page(Relation onerel, Buffer buffer, VacPage vacpage)
 
 /*
  *	scan_index() -- scan one index relation to update statistic.
+ *
+ * We use this when we have no deletions to do.
  */
 static void
 scan_index(Relation indrel, double num_tuples)
 {
-	RetrieveIndexResult res;
-	IndexScanDesc iscan;
-	BlockNumber	nipages;
-	double		nitups;
+	IndexBulkDeleteResult *stats;
 	VacRUsage	ru0;
 
 	vac_init_rusage(&ru0);
 
-	/* walk through the entire index */
-	iscan = index_beginscan(indrel, false, 0, (ScanKey) NULL);
-	nitups = 0;
-
-	while ((res = index_getnext(iscan, ForwardScanDirection))
-		   != (RetrieveIndexResult) NULL)
-	{
-		nitups += 1;
-		pfree(res);
-	}
+	/*
+	 * Even though we're not planning to delete anything, use the
+	 * ambulkdelete call, so that the scan happens within the index AM
+	 * for more speed.
+	 */
+	stats = index_bulk_delete(indrel, dummy_tid_reaped, NULL);
 
-	index_endscan(iscan);
+	if (!stats)
+		return;
 
 	/* now update statistics in pg_class */
-	nipages = RelationGetNumberOfBlocks(indrel);
-	vac_update_relstats(RelationGetRelid(indrel), nipages, nitups, false);
+	vac_update_relstats(RelationGetRelid(indrel),
+						stats->num_pages, stats->num_index_tuples,
+						false);
 
 	elog(MESSAGE_LEVEL, "Index %s: Pages %u; Tuples %.0f.\n\t%s",
-		 RelationGetRelationName(indrel), nipages, nitups,
+		 RelationGetRelationName(indrel),
+		 stats->num_pages, stats->num_index_tuples,
 		 vac_show_rusage(&ru0));
 
 	/*
 	 * Check for tuple count mismatch.  If the index is partial, then
 	 * it's OK for it to have fewer tuples than the heap; else we got trouble.
 	 */
-	if (nitups != num_tuples)
+	if (stats->num_index_tuples != num_tuples)
 	{
-		if (nitups > num_tuples ||
-			! is_partial_index(indrel))
+		if (stats->num_index_tuples > num_tuples ||
+			! vac_is_partial_index(indrel))
 			elog(NOTICE, "Index %s: NUMBER OF INDEX' TUPLES (%.0f) IS NOT THE SAME AS HEAP' (%.0f).\
 \n\tRecreate the index.",
-				 RelationGetRelationName(indrel), nitups, num_tuples);
+				 RelationGetRelationName(indrel),
+				 stats->num_index_tuples, num_tuples);
 	}
+
+	pfree(stats);
 }
 
 /*
@@ -2269,7 +2270,7 @@ vacuum_index(VacPageList vacpagelist, Relation indrel,
 	if (stats->num_index_tuples != num_tuples + keep_tuples)
 	{
 		if (stats->num_index_tuples > num_tuples + keep_tuples ||
-			! is_partial_index(indrel))
+			! vac_is_partial_index(indrel))
 			elog(NOTICE, "Index %s: NUMBER OF INDEX' TUPLES (%.0f) IS NOT THE SAME AS HEAP' (%.0f).\
 \n\tRecreate the index.",
 				 RelationGetRelationName(indrel),
@@ -2331,6 +2332,15 @@ tid_reaped(ItemPointer itemptr, void *state)
 	return true;
 }
 
+/*
+ * Dummy version for scan_index.
+ */
+static bool
+dummy_tid_reaped(ItemPointer itemptr, void *state)
+{
+	return false;
+}
+
 /*
  * Update the shared Free Space Map with the info we now have about
  * free space in the relation, discarding any old info the map may have.
@@ -2552,8 +2562,11 @@ vac_close_indexes(int nindexes, Relation *Irel)
 }
 
 
-static bool
-is_partial_index(Relation indrel)
+/*
+ * Is an index partial (ie, could it contain fewer tuples than the heap?)
+ */
+bool
+vac_is_partial_index(Relation indrel)
 {
 	bool		result;
 	HeapTuple	cachetuple;
@@ -2570,7 +2583,7 @@ is_partial_index(Relation indrel)
 								ObjectIdGetDatum(RelationGetRelid(indrel)),
 								0, 0, 0);
 	if (!HeapTupleIsValid(cachetuple))
-		elog(ERROR, "is_partial_index: index %u not found",
+		elog(ERROR, "vac_is_partial_index: index %u not found",
 			 RelationGetRelid(indrel));
 	indexStruct = (Form_pg_index) GETSTRUCT(cachetuple);
 
diff --git a/src/backend/commands/vacuumlazy.c b/src/backend/commands/vacuumlazy.c
index b78f933f0c3..f509086b52f 100644
--- a/src/backend/commands/vacuumlazy.c
+++ b/src/backend/commands/vacuumlazy.c
@@ -31,7 +31,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/vacuumlazy.c,v 1.2 2001/07/15 22:48:17 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/vacuumlazy.c,v 1.3 2001/07/18 00:46:25 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -101,6 +101,7 @@ static TransactionId XmaxRecent;
 static void lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
 						   Relation *Irel, int nindexes);
 static void lazy_vacuum_heap(Relation onerel, LVRelStats *vacrelstats);
+static void lazy_scan_index(Relation indrel, LVRelStats *vacrelstats);
 static void lazy_vacuum_index(Relation indrel, LVRelStats *vacrelstats);
 static int	lazy_vacuum_page(Relation onerel, BlockNumber blkno, Buffer buffer,
 							 int tupindex, LVRelStats *vacrelstats);
@@ -113,6 +114,7 @@ static void lazy_record_dead_tuple(LVRelStats *vacrelstats,
 static void lazy_record_free_space(LVRelStats *vacrelstats,
 								   BlockNumber page, Size avail);
 static bool lazy_tid_reaped(ItemPointer itemptr, void *state);
+static bool dummy_tid_reaped(ItemPointer itemptr, void *state);
 static void lazy_update_fsm(Relation onerel, LVRelStats *vacrelstats);
 static int	vac_cmp_itemptr(const void *left, const void *right);
 
@@ -197,6 +199,7 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
 				tups_vacuumed,
 				nkeep,
 				nunused;
+	bool		did_vacuum_index = false;
 	int			i;
 	VacRUsage	ru0;
 
@@ -235,6 +238,7 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
 			/* Remove index entries */
 			for (i = 0; i < nindexes; i++)
 				lazy_vacuum_index(Irel[i], vacrelstats);
+			did_vacuum_index = true;
 			/* Remove tuples from heap */
 			lazy_vacuum_heap(onerel, vacrelstats);
 			/* Forget the now-vacuumed tuples, and press on */
@@ -378,6 +382,9 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
 		ReleaseBuffer(buf);
 	}
 
+	/* save stats for use later */
+	vacrelstats->rel_tuples = num_tuples;
+
 	/* If any tuples need to be deleted, perform final vacuum cycle */
 	/* XXX put a threshold on min nuber of tuples here? */
 	if (vacrelstats->num_dead_tuples > 0)
@@ -388,9 +395,12 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
 		/* Remove tuples from heap */
 		lazy_vacuum_heap(onerel, vacrelstats);
 	}
-
-	/* save stats for use later */
-	vacrelstats->rel_tuples = num_tuples;
+	else if (! did_vacuum_index)
+	{
+		/* Scan indexes just to update pg_class statistics about them */
+		for (i = 0; i < nindexes; i++)
+			lazy_scan_index(Irel[i], vacrelstats);
+	}
 
 	elog(MESSAGE_LEVEL, "Pages %u: Changed %u, Empty %u; \
 Tup %.0f: Vac %.0f, Keep %.0f, UnUsed %.0f.\n\tTotal %s",
@@ -495,6 +505,68 @@ lazy_vacuum_page(Relation onerel, BlockNumber blkno, Buffer buffer,
 	return tupindex;
 }
 
+/*
+ *	lazy_scan_index() -- scan one index relation to update pg_class statistic.
+ *
+ * We use this when we have no deletions to do.
+ */
+static void
+lazy_scan_index(Relation indrel, LVRelStats *vacrelstats)
+{
+	IndexBulkDeleteResult *stats;
+	VacRUsage	ru0;
+
+	vac_init_rusage(&ru0);
+
+	/*
+	 * If the index is not partial, skip the scan, and just assume it
+	 * has the same number of tuples as the heap.
+	 */
+	if (! vac_is_partial_index(indrel))
+	{
+		vac_update_relstats(RelationGetRelid(indrel),
+							RelationGetNumberOfBlocks(indrel),
+							vacrelstats->rel_tuples,
+							false);
+		return;
+	}
+
+	/*
+	 * If index is unsafe for concurrent access, must lock it;
+	 * but a shared lock should be sufficient.
+	 */
+	if (! indrel->rd_am->amconcurrent)
+		LockRelation(indrel, AccessShareLock);
+
+	/*
+	 * Even though we're not planning to delete anything, use the
+	 * ambulkdelete call, so that the scan happens within the index AM
+	 * for more speed.
+	 */
+	stats = index_bulk_delete(indrel, dummy_tid_reaped, NULL);
+
+	/*
+	 * Release lock acquired above.
+	 */
+	if (! indrel->rd_am->amconcurrent)
+		UnlockRelation(indrel, AccessShareLock);
+
+	if (!stats)
+		return;
+
+	/* now update statistics in pg_class */
+	vac_update_relstats(RelationGetRelid(indrel),
+						stats->num_pages, stats->num_index_tuples,
+						false);
+
+	elog(MESSAGE_LEVEL, "Index %s: Pages %u; Tuples %.0f.\n\t%s",
+		 RelationGetRelationName(indrel),
+		 stats->num_pages, stats->num_index_tuples,
+		 vac_show_rusage(&ru0));
+
+	pfree(stats);
+}
+
 /*
  *	lazy_vacuum_index() -- vacuum one index relation.
  *
@@ -955,6 +1027,15 @@ lazy_tid_reaped(ItemPointer itemptr, void *state)
 	return (res != NULL);
 }
 
+/*
+ * Dummy version for lazy_scan_index.
+ */
+static bool
+dummy_tid_reaped(ItemPointer itemptr, void *state)
+{
+	return false;
+}
+
 /*
  * Update the shared Free Space Map with the info we now have about
  * free space in the relation, discarding any old info the map may have.
diff --git a/src/include/commands/vacuum.h b/src/include/commands/vacuum.h
index 9fd6513e91e..0d362bb1801 100644
--- a/src/include/commands/vacuum.h
+++ b/src/include/commands/vacuum.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: vacuum.h,v 1.38 2001/07/13 22:55:59 tgl Exp $
+ * $Id: vacuum.h,v 1.39 2001/07/18 00:46:25 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -44,6 +44,7 @@ extern void vac_update_relstats(Oid relid,
 								BlockNumber num_pages,
 								double num_tuples,
 								bool hasindex);
+extern bool vac_is_partial_index(Relation indrel);
 extern void vac_init_rusage(VacRUsage *ru0);
 extern const char *vac_show_rusage(VacRUsage *ru0);
 
-- 
GitLab