From aec4cf1c8c410f9c9db3deabcb94502dcd355b3f Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Wed, 7 Feb 2007 23:11:30 +0000
Subject: [PATCH] Add a function pg_stat_clear_snapshot() that discards any
 statistics snapshot already collected in the current transaction; this allows
 plpgsql functions to watch for stats updates even though they are confined to
 a single transaction. Use this instead of the previous kluge involving
 pg_stat_file() to wait for the stats collector to update in the stats
 regression test.  Internally, decouple storage of stats snapshots from
 transaction boundaries; they'll now stick around until someone calls
 pgstat_clear_snapshot --- which xact.c still does at transaction end, to
 maintain the previous behavior.  This makes the logic a lot cleaner, at the
 price of a couple dozen cycles per transaction exit.

---
 doc/src/sgml/monitoring.sgml        |  20 +++-
 src/backend/access/transam/xact.c   |   5 +-
 src/backend/postmaster/pgstat.c     | 150 ++++++++++++++++------------
 src/backend/utils/adt/pgstatfuncs.c |  36 ++++---
 src/include/catalog/catversion.h    |   4 +-
 src/include/catalog/pg_proc.h       |   8 +-
 src/include/pgstat.h                |   3 +-
 src/test/regress/expected/stats.out |  72 ++++++-------
 src/test/regress/sql/stats.sql      |  56 ++++-------
 9 files changed, 188 insertions(+), 166 deletions(-)

diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 54adf3990b1..4a9732fc39a 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/monitoring.sgml,v 1.45 2007/02/01 00:28:17 momjian Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/monitoring.sgml,v 1.46 2007/02/07 23:11:29 tgl Exp $ -->
 
 <chapter id="monitoring">
  <title>Monitoring Database Activity</title>
@@ -227,7 +227,10 @@ postgres: <replaceable>user</> <replaceable>database</> <replaceable>host</> <re
    queries on the statistics and correlate the results without worrying that
    the numbers are changing underneath you.  But if you want to see new
    results with each query, be sure to do the queries outside any transaction
-   block.
+   block.  Alternatively, you can invoke
+   <function>pg_stat_clear_snapshot</function>(), which will discard the
+   current transaction's statistics snapshot (if any).  The next use of
+   statistical information will cause a new snapshot to be fetched.
   </para>
 
   <table id="monitoring-stats-views-table">
@@ -707,11 +710,20 @@ postgres: <replaceable>user</> <replaceable>database</> <replaceable>host</> <re
       </entry>
      </row>
 
+     <row>
+      <entry><literal><function>pg_stat_clear_snapshot</function>()</literal></entry>
+      <entry><type>void</type></entry>
+      <entry>
+       Discard the current statistics snapshot
+      </entry>
+     </row>
+
      <row>
       <entry><literal><function>pg_stat_reset</function>()</literal></entry>
-      <entry><type>boolean</type></entry>
+      <entry><type>void</type></entry>
       <entry>
-       Reset all block-level and row-level statistics to zero
+       Reset all statistics counters for the current database to zero
+       (requires superuser privileges)
       </entry>
      </row>
     </tbody>
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index 4ee1cc711cd..26a3c5ddc2c 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.232 2007/02/01 19:10:25 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.233 2007/02/07 23:11:29 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1628,6 +1628,7 @@ CommitTransaction(void)
 	AtEOXact_Namespace(true);
 	/* smgrcommit already done */
 	AtEOXact_Files();
+	pgstat_clear_snapshot();
 	pgstat_count_xact_commit();
 	pgstat_report_txn_timestamp(0);
 
@@ -1844,6 +1845,7 @@ PrepareTransaction(void)
 	AtEOXact_Namespace(true);
 	/* smgrcommit already done */
 	AtEOXact_Files();
+	pgstat_clear_snapshot();
 
 	CurrentResourceOwner = NULL;
 	ResourceOwnerDelete(TopTransactionResourceOwner);
@@ -1995,6 +1997,7 @@ AbortTransaction(void)
 	AtEOXact_Namespace(false);
 	smgrabort();
 	AtEOXact_Files();
+	pgstat_clear_snapshot();
 	pgstat_count_xact_rollback();
 	pgstat_report_txn_timestamp(0);
 
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index 6a5907c466e..f64e02020a8 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -13,7 +13,7 @@
  *
  *	Copyright (c) 2001-2007, PostgreSQL Global Development Group
  *
- *	$PostgreSQL: pgsql/src/backend/postmaster/pgstat.c,v 1.144 2007/01/26 20:06:52 tgl Exp $
+ *	$PostgreSQL: pgsql/src/backend/postmaster/pgstat.c,v 1.145 2007/02/07 23:11:29 tgl Exp $
  * ----------
  */
 #include "postgres.h"
@@ -130,9 +130,8 @@ static TabStatArray SharedTabStat = {0, 0, NULL};
 static int	pgStatXactCommit = 0;
 static int	pgStatXactRollback = 0;
 
-static TransactionId pgStatDBHashXact = InvalidTransactionId;
+static MemoryContext pgStatLocalContext = NULL;
 static HTAB *pgStatDBHash = NULL;
-static TransactionId pgStatLocalStatusXact = InvalidTransactionId;
 static PgBackendStatus *localBackendStatusTable = NULL;
 static int	localNumBackends = 0;
 
@@ -156,11 +155,13 @@ static void pgstat_beshutdown_hook(int code, Datum arg);
 static PgStat_StatDBEntry *pgstat_get_db_entry(Oid databaseid, bool create);
 static void pgstat_drop_database(Oid databaseid);
 static void pgstat_write_statsfile(void);
-static void pgstat_read_statsfile(HTAB **dbhash, Oid onlydb);
+static HTAB *pgstat_read_statsfile(Oid onlydb);
 static void backend_read_statsfile(void);
 static void pgstat_read_current_status(void);
 static HTAB *pgstat_collect_oids(Oid catalogid);
 
+static void pgstat_setup_memcxt(void);
+
 static void pgstat_setheader(PgStat_MsgHdr *hdr, StatMsgType mtype);
 static void pgstat_send(void *msg, int len);
 
@@ -1535,22 +1536,24 @@ pgstat_report_waiting(bool waiting)
 static void
 pgstat_read_current_status(void)
 {
-	TransactionId topXid = GetTopTransactionId();
 	volatile PgBackendStatus *beentry;
+	PgBackendStatus *localtable;
 	PgBackendStatus *localentry;
 	int			i;
 
 	Assert(!pgStatRunningInCollector);
-	if (TransactionIdEquals(pgStatLocalStatusXact, topXid))
+	if (localBackendStatusTable)
 		return;					/* already done */
 
-	localBackendStatusTable = (PgBackendStatus *)
-		MemoryContextAlloc(TopTransactionContext,
+	pgstat_setup_memcxt();
+
+	localtable = (PgBackendStatus *)
+		MemoryContextAlloc(pgStatLocalContext,
 						   sizeof(PgBackendStatus) * MaxBackends);
 	localNumBackends = 0;
 
 	beentry = BackendStatusArray;
-	localentry = localBackendStatusTable;
+	localentry = localtable;
 	for (i = 1; i <= MaxBackends; i++)
 	{
 		/*
@@ -1587,7 +1590,8 @@ pgstat_read_current_status(void)
 		}
 	}
 
-	pgStatLocalStatusXact = topXid;
+	/* Set the pointer only after completion of a valid table */
+	localBackendStatusTable = localtable;
 }
 
 
@@ -1720,7 +1724,7 @@ PgstatCollectorMain(int argc, char *argv[])
 	 * zero.
 	 */
 	pgStatRunningInCollector = true;
-	pgstat_read_statsfile(&pgStatDBHash, InvalidOid);
+	pgStatDBHash = pgstat_read_statsfile(InvalidOid);
 
 	/*
 	 * Setup the descriptor set for select(2).	Since only one bit in the set
@@ -2090,38 +2094,24 @@ pgstat_write_statsfile(void)
  *	databases' hash table (whose entries point to the tables' hash tables).
  * ----------
  */
-static void
-pgstat_read_statsfile(HTAB **dbhash, Oid onlydb)
+static HTAB *
+pgstat_read_statsfile(Oid onlydb)
 {
 	PgStat_StatDBEntry *dbentry;
 	PgStat_StatDBEntry dbbuf;
 	PgStat_StatTabEntry *tabentry;
 	PgStat_StatTabEntry tabbuf;
 	HASHCTL		hash_ctl;
+	HTAB	   *dbhash;
 	HTAB	   *tabhash = NULL;
 	FILE	   *fpin;
 	int32		format_id;
 	bool		found;
-	MemoryContext use_mcxt;
-	int			mcxt_flags;
 
 	/*
-	 * If running in the collector or the autovacuum process, we use the
-	 * DynaHashCxt memory context.	If running in a backend, we use the
-	 * TopTransactionContext instead, so the caller must only know the last
-	 * XactId when this call happened to know if his tables are still valid or
-	 * already gone!
+	 * The tables will live in pgStatLocalContext.
 	 */
-	if (pgStatRunningInCollector || IsAutoVacuumProcess())
-	{
-		use_mcxt = NULL;
-		mcxt_flags = 0;
-	}
-	else
-	{
-		use_mcxt = TopTransactionContext;
-		mcxt_flags = HASH_CONTEXT;
-	}
+	pgstat_setup_memcxt();
 
 	/*
 	 * Create the DB hashtable
@@ -2130,9 +2120,9 @@ pgstat_read_statsfile(HTAB **dbhash, Oid onlydb)
 	hash_ctl.keysize = sizeof(Oid);
 	hash_ctl.entrysize = sizeof(PgStat_StatDBEntry);
 	hash_ctl.hash = oid_hash;
-	hash_ctl.hcxt = use_mcxt;
-	*dbhash = hash_create("Databases hash", PGSTAT_DB_HASH_SIZE, &hash_ctl,
-						  HASH_ELEM | HASH_FUNCTION | mcxt_flags);
+	hash_ctl.hcxt = pgStatLocalContext;
+	dbhash = hash_create("Databases hash", PGSTAT_DB_HASH_SIZE, &hash_ctl,
+						 HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT);
 
 	/*
 	 * Try to open the status file. If it doesn't exist, the backends simply
@@ -2140,7 +2130,7 @@ pgstat_read_statsfile(HTAB **dbhash, Oid onlydb)
 	 * with empty counters.
 	 */
 	if ((fpin = AllocateFile(PGSTAT_STAT_FILENAME, PG_BINARY_R)) == NULL)
-		return;
+		return dbhash;
 
 	/*
 	 * Verify it's of the expected format.
@@ -2178,7 +2168,7 @@ pgstat_read_statsfile(HTAB **dbhash, Oid onlydb)
 				/*
 				 * Add to the DB hash
 				 */
-				dbentry = (PgStat_StatDBEntry *) hash_search(*dbhash,
+				dbentry = (PgStat_StatDBEntry *) hash_search(dbhash,
 												  (void *) &dbbuf.databaseid,
 															 HASH_ENTER,
 															 &found);
@@ -2207,11 +2197,11 @@ pgstat_read_statsfile(HTAB **dbhash, Oid onlydb)
 				hash_ctl.keysize = sizeof(Oid);
 				hash_ctl.entrysize = sizeof(PgStat_StatTabEntry);
 				hash_ctl.hash = oid_hash;
-				hash_ctl.hcxt = use_mcxt;
+				hash_ctl.hcxt = pgStatLocalContext;
 				dbentry->tables = hash_create("Per-database table",
 											  PGSTAT_TAB_HASH_SIZE,
 											  &hash_ctl,
-									 HASH_ELEM | HASH_FUNCTION | mcxt_flags);
+									 HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT);
 
 				/*
 				 * Arrange that following 'T's add entries to this database's
@@ -2274,44 +2264,78 @@ pgstat_read_statsfile(HTAB **dbhash, Oid onlydb)
 
 done:
 	FreeFile(fpin);
+
+	return dbhash;
 }
 
 /*
- * If not done for this transaction, read the statistics collector
- * stats file into some hash tables.
- *
- * Because we store the tables in TopTransactionContext, the result
- * is good for the entire current main transaction.
- *
- * Inside the autovacuum process, the statfile is assumed to be valid
- * "forever", that is one iteration, within one database.  This means
- * we only consider the statistics as they were when the autovacuum
- * iteration started.
+ * If not already done, read the statistics collector stats file into
+ * some hash tables.  The results will be kept until pgstat_clear_snapshot()
+ * is called (typically, at end of transaction).
  */
 static void
 backend_read_statsfile(void)
 {
+	/* already read it? */
+	if (pgStatDBHash)
+		return;
+	Assert(!pgStatRunningInCollector);
+
+	/* Autovacuum wants stats about all databases */
 	if (IsAutoVacuumProcess())
-	{
-		/* already read it? */
-		if (pgStatDBHash)
-			return;
-		Assert(!pgStatRunningInCollector);
-		pgstat_read_statsfile(&pgStatDBHash, InvalidOid);
-	}
+		pgStatDBHash = pgstat_read_statsfile(InvalidOid);
 	else
-	{
-		TransactionId topXid = GetTopTransactionId();
+		pgStatDBHash = pgstat_read_statsfile(MyDatabaseId);
+}
 
-		if (!TransactionIdEquals(pgStatDBHashXact, topXid))
-		{
-			Assert(!pgStatRunningInCollector);
-			pgstat_read_statsfile(&pgStatDBHash, MyDatabaseId);
-			pgStatDBHashXact = topXid;
-		}
-	}
+
+/* ----------
+ * pgstat_setup_memcxt() -
+ *
+ *	Create pgStatLocalContext, if not already done.
+ * ----------
+ */
+static void
+pgstat_setup_memcxt(void)
+{
+	if (!pgStatLocalContext)
+		pgStatLocalContext = AllocSetContextCreate(TopMemoryContext,
+												   "Statistics snapshot",
+												   ALLOCSET_SMALL_MINSIZE,
+												   ALLOCSET_SMALL_INITSIZE,
+												   ALLOCSET_SMALL_MAXSIZE);
 }
 
+
+/* ----------
+ * pgstat_clear_snapshot() -
+ *
+ *	Discard any data collected in the current transaction.  Any subsequent
+ *	request will cause new snapshots to be read.
+ *
+ *	This is also invoked during transaction commit or abort to discard
+ *	the no-longer-wanted snapshot.
+ * ----------
+ */
+void
+pgstat_clear_snapshot(void)
+{
+	/* In an autovacuum process we keep the stats forever */
+	if (IsAutoVacuumProcess())
+		return;
+
+	/* Release memory, if any was allocated */
+	if (pgStatLocalContext)
+		MemoryContextDelete(pgStatLocalContext);
+
+	/* Reset variables */
+	pgStatLocalContext = NULL;
+	pgStatDBHash = NULL;
+	localBackendStatusTable = NULL;
+	localNumBackends = 0;
+}
+
+
 /* ----------
  * pgstat_recv_tabstat() -
  *
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 94631969d0e..d011fff76f8 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/utils/adt/pgstatfuncs.c,v 1.37 2007/01/05 22:19:41 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/adt/pgstatfuncs.c,v 1.38 2007/02/07 23:11:29 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -39,7 +39,6 @@ extern Datum pg_stat_get_last_autoanalyze_time(PG_FUNCTION_ARGS);
 
 extern Datum pg_stat_get_backend_idset(PG_FUNCTION_ARGS);
 extern Datum pg_backend_pid(PG_FUNCTION_ARGS);
-extern Datum pg_stat_reset(PG_FUNCTION_ARGS);
 extern Datum pg_stat_get_backend_pid(PG_FUNCTION_ARGS);
 extern Datum pg_stat_get_backend_dbid(PG_FUNCTION_ARGS);
 extern Datum pg_stat_get_backend_userid(PG_FUNCTION_ARGS);
@@ -57,6 +56,9 @@ extern Datum pg_stat_get_db_xact_rollback(PG_FUNCTION_ARGS);
 extern Datum pg_stat_get_db_blocks_fetched(PG_FUNCTION_ARGS);
 extern Datum pg_stat_get_db_blocks_hit(PG_FUNCTION_ARGS);
 
+extern Datum pg_stat_clear_snapshot(PG_FUNCTION_ARGS);
+extern Datum pg_stat_reset(PG_FUNCTION_ARGS);
+
 
 Datum
 pg_stat_get_numscans(PG_FUNCTION_ARGS)
@@ -336,16 +338,6 @@ pg_backend_pid(PG_FUNCTION_ARGS)
 	PG_RETURN_INT32(MyProcPid);
 }
 
-/*
- * Built-in function for resetting the counters
- */
-Datum
-pg_stat_reset(PG_FUNCTION_ARGS)
-{
-	pgstat_reset_counters();
-
-	PG_RETURN_BOOL(true);
-}
 
 Datum
 pg_stat_get_backend_pid(PG_FUNCTION_ARGS)
@@ -678,3 +670,23 @@ pg_stat_get_db_blocks_hit(PG_FUNCTION_ARGS)
 
 	PG_RETURN_INT64(result);
 }
+
+
+/* Discard the active statistics snapshot */
+Datum
+pg_stat_clear_snapshot(PG_FUNCTION_ARGS)
+{
+	pgstat_clear_snapshot();
+
+	PG_RETURN_VOID();
+}
+
+
+/* Reset all counters for the current database */
+Datum
+pg_stat_reset(PG_FUNCTION_ARGS)
+{
+	pgstat_reset_counters();
+
+	PG_RETURN_VOID();
+}
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 154b56b32dd..9ebc4e9b9fd 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -37,7 +37,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.381 2007/02/06 02:59:12 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.382 2007/02/07 23:11:29 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -53,6 +53,6 @@
  */
 
 /*							yyyymmddN */
-#define CATALOG_VERSION_NO	200702051
+#define CATALOG_VERSION_NO	200702071
 
 #endif
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 500d2395422..e8014c2bda8 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.442 2007/02/03 14:06:55 petere Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.443 2007/02/07 23:11:30 tgl Exp $
  *
  * NOTES
  *	  The script catalog/genbki.sh reads this file and generates .bki
@@ -2938,8 +2938,6 @@ DATA(insert OID = 1936 (  pg_stat_get_backend_idset		PGNSP PGUID 12 1 100 f f t
 DESCR("Statistics: Currently active backend IDs");
 DATA(insert OID = 2026 (  pg_backend_pid				PGNSP PGUID 12 1 0 f f t f s 0 23 "" _null_ _null_ _null_ pg_backend_pid - _null_ ));
 DESCR("Statistics: Current backend PID");
-DATA(insert OID = 2274 (  pg_stat_reset				PGNSP PGUID 12 1 0 f f f f v 0 16  "" _null_ _null_ _null_	pg_stat_reset - _null_ ));
-DESCR("Statistics: Reset collected statistics");
 DATA(insert OID = 1937 (  pg_stat_get_backend_pid		PGNSP PGUID 12 1 0 f f t f s 1 23 "23" _null_ _null_ _null_ pg_stat_get_backend_pid - _null_ ));
 DESCR("Statistics: PID of backend");
 DATA(insert OID = 1938 (  pg_stat_get_backend_dbid		PGNSP PGUID 12 1 0 f f t f s 1 26 "23" _null_ _null_ _null_ pg_stat_get_backend_dbid - _null_ ));
@@ -2970,6 +2968,10 @@ DATA(insert OID = 1944 (  pg_stat_get_db_blocks_fetched PGNSP PGUID 12 1 0 f f t
 DESCR("Statistics: Blocks fetched for database");
 DATA(insert OID = 1945 (  pg_stat_get_db_blocks_hit		PGNSP PGUID 12 1 0 f f t f s 1 20 "26" _null_ _null_ _null_ pg_stat_get_db_blocks_hit - _null_ ));
 DESCR("Statistics: Blocks found in cache for database");
+DATA(insert OID = 2230 (  pg_stat_clear_snapshot		PGNSP PGUID 12 1 0 f f f f v 0 2278  "" _null_ _null_ _null_	pg_stat_clear_snapshot - _null_ ));
+DESCR("Statistics: Discard current transaction's statistics snapshot");
+DATA(insert OID = 2274 (  pg_stat_reset					PGNSP PGUID 12 1 0 f f f f v 0 2278  "" _null_ _null_ _null_	pg_stat_reset - _null_ ));
+DESCR("Statistics: Reset collected statistics for current database");
 
 DATA(insert OID = 1946 (  encode						PGNSP PGUID 12 1 0 f f t f i 2 25 "17 25" _null_ _null_ _null_	binary_encode - _null_ ));
 DESCR("Convert bytea value into some ascii-only text string");
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index e64fb8bf550..896cc5ad68c 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -5,7 +5,7 @@
  *
  *	Copyright (c) 2001-2007, PostgreSQL Global Development Group
  *
- *	$PostgreSQL: pgsql/src/include/pgstat.h,v 1.52 2007/01/05 22:19:50 momjian Exp $
+ *	$PostgreSQL: pgsql/src/include/pgstat.h,v 1.53 2007/02/07 23:11:30 tgl Exp $
  * ----------
  */
 #ifndef PGSTAT_H
@@ -380,6 +380,7 @@ extern void pgstat_report_tabstat(void);
 extern void pgstat_vacuum_tabstat(void);
 extern void pgstat_drop_relation(Oid relid);
 
+extern void pgstat_clear_snapshot(void);
 extern void pgstat_reset_counters(void);
 
 extern void pgstat_report_autovac(Oid dboid);
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index d6b17157b06..fc76d142259 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -27,61 +27,28 @@ SELECT t.seq_scan, t.seq_tup_read, t.idx_scan, t.idx_tup_fetch,
   FROM pg_catalog.pg_stat_user_tables AS t,
        pg_catalog.pg_statio_user_tables AS b
  WHERE t.relname='tenk2' AND b.relname='tenk2';
--- enable statistics
-SET stats_block_level = on;
-SET stats_row_level = on;
--- do a seqscan
-SELECT count(*) FROM tenk2;
- count 
--------
- 10000
-(1 row)
-
--- do an indexscan
-SELECT count(*) FROM tenk2 WHERE unique1 = 1;
- count 
--------
-     1
-(1 row)
-
--- All of the thrashing here is to wait for the stats collector to update,
--- without waiting too long (in fact, we'd like to try to measure how long
--- we wait).  Watching for change in the stats themselves wouldn't work
--- because the backend only reads them once per transaction.  The stats file
--- mod timestamp isn't too helpful because it may have resolution of only one
--- second, or even worse.  So, we touch a new table and then watch for change
--- in the size of the stats file.  Ugh.
--- save current stats-file size
-CREATE TEMP TABLE prevfilesize AS
-  SELECT size FROM pg_stat_file('global/pgstat.stat');
--- make and touch a previously nonexistent table
-CREATE TABLE stats_hack (f1 int);
-SELECT * FROM stats_hack;
- f1 
-----
-(0 rows)
-
--- wait for stats collector to update
+-- function to wait for counters to advance
 create function wait_for_stats() returns void as $$
 declare
   start_time timestamptz := clock_timestamp();
-  oldsize bigint;
-  newsize bigint;
+  updated bool;
 begin
-  -- fetch previous stats-file size
-  select size into oldsize from prevfilesize;
-
   -- we don't want to wait forever; loop will exit after 30 seconds
   for i in 1 .. 300 loop
 
-    -- look for update of stats file
-    select size into newsize from pg_stat_file('global/pgstat.stat');
+    -- check to see if indexscan has been sensed
+    SELECT (st.idx_scan >= pr.idx_scan + 1) INTO updated
+      FROM pg_stat_user_tables AS st, pg_class AS cl, prevstats AS pr
+     WHERE st.relname='tenk2' AND cl.relname='tenk2';
 
-    exit when newsize != oldsize;
+    exit when updated;
 
     -- wait a little
     perform pg_sleep(0.1);
 
+    -- reset stats snapshot so we can test again
+    perform pg_stat_clear_snapshot();
+
   end loop;
 
   -- report time waited in postmaster log (where it won't change test output)
@@ -89,13 +56,30 @@ begin
     extract(epoch from clock_timestamp() - start_time);
 end
 $$ language plpgsql;
+-- enable statistics
+SET stats_block_level = on;
+SET stats_row_level = on;
+-- do a seqscan
+SELECT count(*) FROM tenk2;
+ count 
+-------
+ 10000
+(1 row)
+
+-- do an indexscan
+SELECT count(*) FROM tenk2 WHERE unique1 = 1;
+ count 
+-------
+     1
+(1 row)
+
+-- wait for stats collector to update
 SELECT wait_for_stats();
  wait_for_stats 
 ----------------
  
 (1 row)
 
-DROP TABLE stats_hack;
 -- check effects
 SELECT st.seq_scan >= pr.seq_scan + 1,
        st.seq_tup_read >= pr.seq_tup_read + cl.reltuples,
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index dca0031470b..cde38b3a379 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -21,52 +21,28 @@ SELECT t.seq_scan, t.seq_tup_read, t.idx_scan, t.idx_tup_fetch,
        pg_catalog.pg_statio_user_tables AS b
  WHERE t.relname='tenk2' AND b.relname='tenk2';
 
--- enable statistics
-SET stats_block_level = on;
-SET stats_row_level = on;
-
--- do a seqscan
-SELECT count(*) FROM tenk2;
--- do an indexscan
-SELECT count(*) FROM tenk2 WHERE unique1 = 1;
-
--- All of the thrashing here is to wait for the stats collector to update,
--- without waiting too long (in fact, we'd like to try to measure how long
--- we wait).  Watching for change in the stats themselves wouldn't work
--- because the backend only reads them once per transaction.  The stats file
--- mod timestamp isn't too helpful because it may have resolution of only one
--- second, or even worse.  So, we touch a new table and then watch for change
--- in the size of the stats file.  Ugh.
-
--- save current stats-file size
-CREATE TEMP TABLE prevfilesize AS
-  SELECT size FROM pg_stat_file('global/pgstat.stat');
-
--- make and touch a previously nonexistent table
-CREATE TABLE stats_hack (f1 int);
-SELECT * FROM stats_hack;
-
--- wait for stats collector to update
+-- function to wait for counters to advance
 create function wait_for_stats() returns void as $$
 declare
   start_time timestamptz := clock_timestamp();
-  oldsize bigint;
-  newsize bigint;
+  updated bool;
 begin
-  -- fetch previous stats-file size
-  select size into oldsize from prevfilesize;
-
   -- we don't want to wait forever; loop will exit after 30 seconds
   for i in 1 .. 300 loop
 
-    -- look for update of stats file
-    select size into newsize from pg_stat_file('global/pgstat.stat');
+    -- check to see if indexscan has been sensed
+    SELECT (st.idx_scan >= pr.idx_scan + 1) INTO updated
+      FROM pg_stat_user_tables AS st, pg_class AS cl, prevstats AS pr
+     WHERE st.relname='tenk2' AND cl.relname='tenk2';
 
-    exit when newsize != oldsize;
+    exit when updated;
 
     -- wait a little
     perform pg_sleep(0.1);
 
+    -- reset stats snapshot so we can test again
+    perform pg_stat_clear_snapshot();
+
   end loop;
 
   -- report time waited in postmaster log (where it won't change test output)
@@ -75,9 +51,17 @@ begin
 end
 $$ language plpgsql;
 
-SELECT wait_for_stats();
+-- enable statistics
+SET stats_block_level = on;
+SET stats_row_level = on;
 
-DROP TABLE stats_hack;
+-- do a seqscan
+SELECT count(*) FROM tenk2;
+-- do an indexscan
+SELECT count(*) FROM tenk2 WHERE unique1 = 1;
+
+-- wait for stats collector to update
+SELECT wait_for_stats();
 
 -- check effects
 SELECT st.seq_scan >= pr.seq_scan + 1,
-- 
GitLab