From 58a2a22c689f4ec6aec056ae5c3702bf5c9bf148 Mon Sep 17 00:00:00 2001
From: Andres Freund <andres@anarazel.de>
Date: Wed, 2 Mar 2022 18:02:03 -0800
Subject: [PATCH v65 09/11] pgstat: Extend pgstat test coverage.

---
 src/test/isolation/expected/stats.out | 1773 +++++++++++++++++++++++++
 src/test/isolation/isolation_schedule |    2 +
 src/test/isolation/specs/stats.spec   |  282 ++++
 src/test/regress/expected/stats.out   |  380 ++++++
 src/test/regress/sql/stats.sql        |  162 +++
 5 files changed, 2599 insertions(+)
 create mode 100644 src/test/isolation/expected/stats.out
 create mode 100644 src/test/isolation/specs/stats.spec

diff --git a/src/test/isolation/expected/stats.out b/src/test/isolation/expected/stats.out
new file mode 100644
index 00000000000..301cc6faf57
--- /dev/null
+++ b/src/test/isolation/expected/stats.out
@@ -0,0 +1,1773 @@
+Parsed test spec with 2 sessions
+
+starting permutation: s1_track_funcs_none s1_func_stats s1_func_call s1_func_call s1_ff s1_func_stats
+step s1_track_funcs_none: SET track_functions = 'none';
+step s1_func_stats: 
+    SELECT
+        tso.name,
+        pg_stat_get_function_calls(tso.oid),
+        pg_stat_get_function_total_time(tso.oid) > 0 total_above_zero,
+        pg_stat_get_function_self_time(tso.oid) > 0 self_above_zero
+    FROM test_stat_oid AS tso
+    WHERE tso.name = 'test_stat_func'
+
+name          |pg_stat_get_function_calls|total_above_zero|self_above_zero
+--------------+--------------------------+----------------+---------------
+test_stat_func|                          |                |               
+(1 row)
+
+step s1_func_call: SELECT test_stat_func();
+test_stat_func
+--------------
+              
+(1 row)
+
+step s1_func_call: SELECT test_stat_func();
+test_stat_func
+--------------
+              
+(1 row)
+
+step s1_ff: SELECT pg_stat_force_next_flush();
+pg_stat_force_next_flush
+------------------------
+                        
+(1 row)
+
+step s1_func_stats: 
+    SELECT
+        tso.name,
+        pg_stat_get_function_calls(tso.oid),
+        pg_stat_get_function_total_time(tso.oid) > 0 total_above_zero,
+        pg_stat_get_function_self_time(tso.oid) > 0 self_above_zero
+    FROM test_stat_oid AS tso
+    WHERE tso.name = 'test_stat_func'
+
+name          |pg_stat_get_function_calls|total_above_zero|self_above_zero
+--------------+--------------------------+----------------+---------------
+test_stat_func|                          |                |               
+(1 row)
+
+
+starting permutation: s1_track_funcs_all s1_func_stats s1_func_call s1_func_call s1_ff s1_func_stats
+step s1_track_funcs_all: SET track_functions = 'all';
+step s1_func_stats: 
+    SELECT
+        tso.name,
+        pg_stat_get_function_calls(tso.oid),
+        pg_stat_get_function_total_time(tso.oid) > 0 total_above_zero,
+        pg_stat_get_function_self_time(tso.oid) > 0 self_above_zero
+    FROM test_stat_oid AS tso
+    WHERE tso.name = 'test_stat_func'
+
+name          |pg_stat_get_function_calls|total_above_zero|self_above_zero
+--------------+--------------------------+----------------+---------------
+test_stat_func|                          |                |               
+(1 row)
+
+step s1_func_call: SELECT test_stat_func();
+test_stat_func
+--------------
+              
+(1 row)
+
+step s1_func_call: SELECT test_stat_func();
+test_stat_func
+--------------
+              
+(1 row)
+
+step s1_ff: SELECT pg_stat_force_next_flush();
+pg_stat_force_next_flush
+------------------------
+                        
+(1 row)
+
+step s1_func_stats: 
+    SELECT
+        tso.name,
+        pg_stat_get_function_calls(tso.oid),
+        pg_stat_get_function_total_time(tso.oid) > 0 total_above_zero,
+        pg_stat_get_function_self_time(tso.oid) > 0 self_above_zero
+    FROM test_stat_oid AS tso
+    WHERE tso.name = 'test_stat_func'
+
+name          |pg_stat_get_function_calls|total_above_zero|self_above_zero
+--------------+--------------------------+----------------+---------------
+test_stat_func|                         2|t               |t              
+(1 row)
+
+
+starting permutation: s1_track_funcs_all s2_track_funcs_all s1_func_stats s2_func_stats s1_func_call s2_func_call s1_func_call s2_func_call s2_func_call s1_ff s2_ff s1_func_stats s2_func_stats
+step s1_track_funcs_all: SET track_functions = 'all';
+step s2_track_funcs_all: SET track_functions = 'all';
+step s1_func_stats: 
+    SELECT
+        tso.name,
+        pg_stat_get_function_calls(tso.oid),
+        pg_stat_get_function_total_time(tso.oid) > 0 total_above_zero,
+        pg_stat_get_function_self_time(tso.oid) > 0 self_above_zero
+    FROM test_stat_oid AS tso
+    WHERE tso.name = 'test_stat_func'
+
+name          |pg_stat_get_function_calls|total_above_zero|self_above_zero
+--------------+--------------------------+----------------+---------------
+test_stat_func|                          |                |               
+(1 row)
+
+step s2_func_stats: 
+    SELECT
+        tso.name,
+        pg_stat_get_function_calls(tso.oid),
+        pg_stat_get_function_total_time(tso.oid) > 0 total_above_zero,
+        pg_stat_get_function_self_time(tso.oid) > 0 self_above_zero
+    FROM test_stat_oid AS tso
+    WHERE tso.name = 'test_stat_func'
+
+name          |pg_stat_get_function_calls|total_above_zero|self_above_zero
+--------------+--------------------------+----------------+---------------
+test_stat_func|                          |                |               
+(1 row)
+
+step s1_func_call: SELECT test_stat_func();
+test_stat_func
+--------------
+              
+(1 row)
+
+step s2_func_call: SELECT test_stat_func()
+test_stat_func
+--------------
+              
+(1 row)
+
+step s1_func_call: SELECT test_stat_func();
+test_stat_func
+--------------
+              
+(1 row)
+
+step s2_func_call: SELECT test_stat_func()
+test_stat_func
+--------------
+              
+(1 row)
+
+step s2_func_call: SELECT test_stat_func()
+test_stat_func
+--------------
+              
+(1 row)
+
+step s1_ff: SELECT pg_stat_force_next_flush();
+pg_stat_force_next_flush
+------------------------
+                        
+(1 row)
+
+step s2_ff: SELECT pg_stat_force_next_flush();
+pg_stat_force_next_flush
+------------------------
+                        
+(1 row)
+
+step s1_func_stats: 
+    SELECT
+        tso.name,
+        pg_stat_get_function_calls(tso.oid),
+        pg_stat_get_function_total_time(tso.oid) > 0 total_above_zero,
+        pg_stat_get_function_self_time(tso.oid) > 0 self_above_zero
+    FROM test_stat_oid AS tso
+    WHERE tso.name = 'test_stat_func'
+
+name          |pg_stat_get_function_calls|total_above_zero|self_above_zero
+--------------+--------------------------+----------------+---------------
+test_stat_func|                         5|t               |t              
+(1 row)
+
+step s2_func_stats: 
+    SELECT
+        tso.name,
+        pg_stat_get_function_calls(tso.oid),
+        pg_stat_get_function_total_time(tso.oid) > 0 total_above_zero,
+        pg_stat_get_function_self_time(tso.oid) > 0 self_above_zero
+    FROM test_stat_oid AS tso
+    WHERE tso.name = 'test_stat_func'
+
+name          |pg_stat_get_function_calls|total_above_zero|self_above_zero
+--------------+--------------------------+----------------+---------------
+test_stat_func|                         5|t               |t              
+(1 row)
+
+
+starting permutation: s1_track_funcs_all s2_track_funcs_all s1_func_stats s2_func_stats s1_func_call s1_ff s2_func_call s2_func_call s2_ff s1_func_stats s2_func_stats
+step s1_track_funcs_all: SET track_functions = 'all';
+step s2_track_funcs_all: SET track_functions = 'all';
+step s1_func_stats: 
+    SELECT
+        tso.name,
+        pg_stat_get_function_calls(tso.oid),
+        pg_stat_get_function_total_time(tso.oid) > 0 total_above_zero,
+        pg_stat_get_function_self_time(tso.oid) > 0 self_above_zero
+    FROM test_stat_oid AS tso
+    WHERE tso.name = 'test_stat_func'
+
+name          |pg_stat_get_function_calls|total_above_zero|self_above_zero
+--------------+--------------------------+----------------+---------------
+test_stat_func|                          |                |               
+(1 row)
+
+step s2_func_stats: 
+    SELECT
+        tso.name,
+        pg_stat_get_function_calls(tso.oid),
+        pg_stat_get_function_total_time(tso.oid) > 0 total_above_zero,
+        pg_stat_get_function_self_time(tso.oid) > 0 self_above_zero
+    FROM test_stat_oid AS tso
+    WHERE tso.name = 'test_stat_func'
+
+name          |pg_stat_get_function_calls|total_above_zero|self_above_zero
+--------------+--------------------------+----------------+---------------
+test_stat_func|                          |                |               
+(1 row)
+
+step s1_func_call: SELECT test_stat_func();
+test_stat_func
+--------------
+              
+(1 row)
+
+step s1_ff: SELECT pg_stat_force_next_flush();
+pg_stat_force_next_flush
+------------------------
+                        
+(1 row)
+
+step s2_func_call: SELECT test_stat_func()
+test_stat_func
+--------------
+              
+(1 row)
+
+step s2_func_call: SELECT test_stat_func()
+test_stat_func
+--------------
+              
+(1 row)
+
+step s2_ff: SELECT pg_stat_force_next_flush();
+pg_stat_force_next_flush
+------------------------
+                        
+(1 row)
+
+step s1_func_stats: 
+    SELECT
+        tso.name,
+        pg_stat_get_function_calls(tso.oid),
+        pg_stat_get_function_total_time(tso.oid) > 0 total_above_zero,
+        pg_stat_get_function_self_time(tso.oid) > 0 self_above_zero
+    FROM test_stat_oid AS tso
+    WHERE tso.name = 'test_stat_func'
+
+name          |pg_stat_get_function_calls|total_above_zero|self_above_zero
+--------------+--------------------------+----------------+---------------
+test_stat_func|                         3|t               |t              
+(1 row)
+
+step s2_func_stats: 
+    SELECT
+        tso.name,
+        pg_stat_get_function_calls(tso.oid),
+        pg_stat_get_function_total_time(tso.oid) > 0 total_above_zero,
+        pg_stat_get_function_self_time(tso.oid) > 0 self_above_zero
+    FROM test_stat_oid AS tso
+    WHERE tso.name = 'test_stat_func'
+
+name          |pg_stat_get_function_calls|total_above_zero|self_above_zero
+--------------+--------------------------+----------------+---------------
+test_stat_func|                         3|t               |t              
+(1 row)
+
+
+starting permutation: s1_track_funcs_all s2_track_funcs_all s1_func_stats s2_func_stats s1_begin s1_func_call s1_func_call s1_commit s1_ff s1_func_stats s2_func_stats
+step s1_track_funcs_all: SET track_functions = 'all';
+step s2_track_funcs_all: SET track_functions = 'all';
+step s1_func_stats: 
+    SELECT
+        tso.name,
+        pg_stat_get_function_calls(tso.oid),
+        pg_stat_get_function_total_time(tso.oid) > 0 total_above_zero,
+        pg_stat_get_function_self_time(tso.oid) > 0 self_above_zero
+    FROM test_stat_oid AS tso
+    WHERE tso.name = 'test_stat_func'
+
+name          |pg_stat_get_function_calls|total_above_zero|self_above_zero
+--------------+--------------------------+----------------+---------------
+test_stat_func|                          |                |               
+(1 row)
+
+step s2_func_stats: 
+    SELECT
+        tso.name,
+        pg_stat_get_function_calls(tso.oid),
+        pg_stat_get_function_total_time(tso.oid) > 0 total_above_zero,
+        pg_stat_get_function_self_time(tso.oid) > 0 self_above_zero
+    FROM test_stat_oid AS tso
+    WHERE tso.name = 'test_stat_func'
+
+name          |pg_stat_get_function_calls|total_above_zero|self_above_zero
+--------------+--------------------------+----------------+---------------
+test_stat_func|                          |                |               
+(1 row)
+
+step s1_begin: BEGIN;
+step s1_func_call: SELECT test_stat_func();
+test_stat_func
+--------------
+              
+(1 row)
+
+step s1_func_call: SELECT test_stat_func();
+test_stat_func
+--------------
+              
+(1 row)
+
+step s1_commit: COMMIT;
+step s1_ff: SELECT pg_stat_force_next_flush();
+pg_stat_force_next_flush
+------------------------
+                        
+(1 row)
+
+step s1_func_stats: 
+    SELECT
+        tso.name,
+        pg_stat_get_function_calls(tso.oid),
+        pg_stat_get_function_total_time(tso.oid) > 0 total_above_zero,
+        pg_stat_get_function_self_time(tso.oid) > 0 self_above_zero
+    FROM test_stat_oid AS tso
+    WHERE tso.name = 'test_stat_func'
+
+name          |pg_stat_get_function_calls|total_above_zero|self_above_zero
+--------------+--------------------------+----------------+---------------
+test_stat_func|                         2|t               |t              
+(1 row)
+
+step s2_func_stats: 
+    SELECT
+        tso.name,
+        pg_stat_get_function_calls(tso.oid),
+        pg_stat_get_function_total_time(tso.oid) > 0 total_above_zero,
+        pg_stat_get_function_self_time(tso.oid) > 0 self_above_zero
+    FROM test_stat_oid AS tso
+    WHERE tso.name = 'test_stat_func'
+
+name          |pg_stat_get_function_calls|total_above_zero|self_above_zero
+--------------+--------------------------+----------------+---------------
+test_stat_func|                         2|t               |t              
+(1 row)
+
+
+starting permutation: s1_track_funcs_all s2_track_funcs_all s1_func_stats s2_func_stats s1_begin s1_func_call s2_func_call s1_func_drop s2_func_call s2_ff s2_func_stats s1_commit s1_ff s1_func_stats s2_func_stats
+step s1_track_funcs_all: SET track_functions = 'all';
+step s2_track_funcs_all: SET track_functions = 'all';
+step s1_func_stats: 
+    SELECT
+        tso.name,
+        pg_stat_get_function_calls(tso.oid),
+        pg_stat_get_function_total_time(tso.oid) > 0 total_above_zero,
+        pg_stat_get_function_self_time(tso.oid) > 0 self_above_zero
+    FROM test_stat_oid AS tso
+    WHERE tso.name = 'test_stat_func'
+
+name          |pg_stat_get_function_calls|total_above_zero|self_above_zero
+--------------+--------------------------+----------------+---------------
+test_stat_func|                          |                |               
+(1 row)
+
+step s2_func_stats: 
+    SELECT
+        tso.name,
+        pg_stat_get_function_calls(tso.oid),
+        pg_stat_get_function_total_time(tso.oid) > 0 total_above_zero,
+        pg_stat_get_function_self_time(tso.oid) > 0 self_above_zero
+    FROM test_stat_oid AS tso
+    WHERE tso.name = 'test_stat_func'
+
+name          |pg_stat_get_function_calls|total_above_zero|self_above_zero
+--------------+--------------------------+----------------+---------------
+test_stat_func|                          |                |               
+(1 row)
+
+step s1_begin: BEGIN;
+step s1_func_call: SELECT test_stat_func();
+test_stat_func
+--------------
+              
+(1 row)
+
+step s2_func_call: SELECT test_stat_func()
+test_stat_func
+--------------
+              
+(1 row)
+
+step s1_func_drop: DROP FUNCTION test_stat_func();
+step s2_func_call: SELECT test_stat_func()
+test_stat_func
+--------------
+              
+(1 row)
+
+step s2_ff: SELECT pg_stat_force_next_flush();
+pg_stat_force_next_flush
+------------------------
+                        
+(1 row)
+
+step s2_func_stats: 
+    SELECT
+        tso.name,
+        pg_stat_get_function_calls(tso.oid),
+        pg_stat_get_function_total_time(tso.oid) > 0 total_above_zero,
+        pg_stat_get_function_self_time(tso.oid) > 0 self_above_zero
+    FROM test_stat_oid AS tso
+    WHERE tso.name = 'test_stat_func'
+
+name          |pg_stat_get_function_calls|total_above_zero|self_above_zero
+--------------+--------------------------+----------------+---------------
+test_stat_func|                         2|t               |t              
+(1 row)
+
+step s1_commit: COMMIT;
+step s1_ff: SELECT pg_stat_force_next_flush();
+pg_stat_force_next_flush
+------------------------
+                        
+(1 row)
+
+step s1_func_stats: 
+    SELECT
+        tso.name,
+        pg_stat_get_function_calls(tso.oid),
+        pg_stat_get_function_total_time(tso.oid) > 0 total_above_zero,
+        pg_stat_get_function_self_time(tso.oid) > 0 self_above_zero
+    FROM test_stat_oid AS tso
+    WHERE tso.name = 'test_stat_func'
+
+name          |pg_stat_get_function_calls|total_above_zero|self_above_zero
+--------------+--------------------------+----------------+---------------
+test_stat_func|                          |                |               
+(1 row)
+
+step s2_func_stats: 
+    SELECT
+        tso.name,
+        pg_stat_get_function_calls(tso.oid),
+        pg_stat_get_function_total_time(tso.oid) > 0 total_above_zero,
+        pg_stat_get_function_self_time(tso.oid) > 0 self_above_zero
+    FROM test_stat_oid AS tso
+    WHERE tso.name = 'test_stat_func'
+
+name          |pg_stat_get_function_calls|total_above_zero|self_above_zero
+--------------+--------------------------+----------------+---------------
+test_stat_func|                          |                |               
+(1 row)
+
+
+starting permutation: s1_track_funcs_all s2_track_funcs_all s1_func_stats s2_func_stats s1_begin s1_func_call s2_func_call s1_func_drop s2_func_call s2_ff s2_func_stats s1_rollback s1_ff s1_func_stats s2_func_stats
+step s1_track_funcs_all: SET track_functions = 'all';
+step s2_track_funcs_all: SET track_functions = 'all';
+step s1_func_stats: 
+    SELECT
+        tso.name,
+        pg_stat_get_function_calls(tso.oid),
+        pg_stat_get_function_total_time(tso.oid) > 0 total_above_zero,
+        pg_stat_get_function_self_time(tso.oid) > 0 self_above_zero
+    FROM test_stat_oid AS tso
+    WHERE tso.name = 'test_stat_func'
+
+name          |pg_stat_get_function_calls|total_above_zero|self_above_zero
+--------------+--------------------------+----------------+---------------
+test_stat_func|                          |                |               
+(1 row)
+
+step s2_func_stats: 
+    SELECT
+        tso.name,
+        pg_stat_get_function_calls(tso.oid),
+        pg_stat_get_function_total_time(tso.oid) > 0 total_above_zero,
+        pg_stat_get_function_self_time(tso.oid) > 0 self_above_zero
+    FROM test_stat_oid AS tso
+    WHERE tso.name = 'test_stat_func'
+
+name          |pg_stat_get_function_calls|total_above_zero|self_above_zero
+--------------+--------------------------+----------------+---------------
+test_stat_func|                          |                |               
+(1 row)
+
+step s1_begin: BEGIN;
+step s1_func_call: SELECT test_stat_func();
+test_stat_func
+--------------
+              
+(1 row)
+
+step s2_func_call: SELECT test_stat_func()
+test_stat_func
+--------------
+              
+(1 row)
+
+step s1_func_drop: DROP FUNCTION test_stat_func();
+step s2_func_call: SELECT test_stat_func()
+test_stat_func
+--------------
+              
+(1 row)
+
+step s2_ff: SELECT pg_stat_force_next_flush();
+pg_stat_force_next_flush
+------------------------
+                        
+(1 row)
+
+step s2_func_stats: 
+    SELECT
+        tso.name,
+        pg_stat_get_function_calls(tso.oid),
+        pg_stat_get_function_total_time(tso.oid) > 0 total_above_zero,
+        pg_stat_get_function_self_time(tso.oid) > 0 self_above_zero
+    FROM test_stat_oid AS tso
+    WHERE tso.name = 'test_stat_func'
+
+name          |pg_stat_get_function_calls|total_above_zero|self_above_zero
+--------------+--------------------------+----------------+---------------
+test_stat_func|                         2|t               |t              
+(1 row)
+
+step s1_rollback: ROLLBACK;
+step s1_ff: SELECT pg_stat_force_next_flush();
+pg_stat_force_next_flush
+------------------------
+                        
+(1 row)
+
+step s1_func_stats: 
+    SELECT
+        tso.name,
+        pg_stat_get_function_calls(tso.oid),
+        pg_stat_get_function_total_time(tso.oid) > 0 total_above_zero,
+        pg_stat_get_function_self_time(tso.oid) > 0 self_above_zero
+    FROM test_stat_oid AS tso
+    WHERE tso.name = 'test_stat_func'
+
+name          |pg_stat_get_function_calls|total_above_zero|self_above_zero
+--------------+--------------------------+----------------+---------------
+test_stat_func|                         3|t               |t              
+(1 row)
+
+step s2_func_stats: 
+    SELECT
+        tso.name,
+        pg_stat_get_function_calls(tso.oid),
+        pg_stat_get_function_total_time(tso.oid) > 0 total_above_zero,
+        pg_stat_get_function_self_time(tso.oid) > 0 self_above_zero
+    FROM test_stat_oid AS tso
+    WHERE tso.name = 'test_stat_func'
+
+name          |pg_stat_get_function_calls|total_above_zero|self_above_zero
+--------------+--------------------------+----------------+---------------
+test_stat_func|                         3|t               |t              
+(1 row)
+
+
+starting permutation: s1_track_funcs_all s2_track_funcs_all s2_func_call s2_ff s2_begin s2_func_call s1_func_drop s1_func_stats s2_commit s2_ff s1_func_stats s2_func_stats
+step s1_track_funcs_all: SET track_functions = 'all';
+step s2_track_funcs_all: SET track_functions = 'all';
+step s2_func_call: SELECT test_stat_func()
+test_stat_func
+--------------
+              
+(1 row)
+
+step s2_ff: SELECT pg_stat_force_next_flush();
+pg_stat_force_next_flush
+------------------------
+                        
+(1 row)
+
+step s2_begin: BEGIN;
+step s2_func_call: SELECT test_stat_func()
+test_stat_func
+--------------
+              
+(1 row)
+
+step s1_func_drop: DROP FUNCTION test_stat_func();
+step s1_func_stats: 
+    SELECT
+        tso.name,
+        pg_stat_get_function_calls(tso.oid),
+        pg_stat_get_function_total_time(tso.oid) > 0 total_above_zero,
+        pg_stat_get_function_self_time(tso.oid) > 0 self_above_zero
+    FROM test_stat_oid AS tso
+    WHERE tso.name = 'test_stat_func'
+
+name          |pg_stat_get_function_calls|total_above_zero|self_above_zero
+--------------+--------------------------+----------------+---------------
+test_stat_func|                          |                |               
+(1 row)
+
+step s2_commit: COMMIT;
+step s2_ff: SELECT pg_stat_force_next_flush();
+pg_stat_force_next_flush
+------------------------
+                        
+(1 row)
+
+step s1_func_stats: 
+    SELECT
+        tso.name,
+        pg_stat_get_function_calls(tso.oid),
+        pg_stat_get_function_total_time(tso.oid) > 0 total_above_zero,
+        pg_stat_get_function_self_time(tso.oid) > 0 self_above_zero
+    FROM test_stat_oid AS tso
+    WHERE tso.name = 'test_stat_func'
+
+name          |pg_stat_get_function_calls|total_above_zero|self_above_zero
+--------------+--------------------------+----------------+---------------
+test_stat_func|                          |                |               
+(1 row)
+
+step s2_func_stats: 
+    SELECT
+        tso.name,
+        pg_stat_get_function_calls(tso.oid),
+        pg_stat_get_function_total_time(tso.oid) > 0 total_above_zero,
+        pg_stat_get_function_self_time(tso.oid) > 0 self_above_zero
+    FROM test_stat_oid AS tso
+    WHERE tso.name = 'test_stat_func'
+
+name          |pg_stat_get_function_calls|total_above_zero|self_above_zero
+--------------+--------------------------+----------------+---------------
+test_stat_func|                          |                |               
+(1 row)
+
+
+starting permutation: s1_track_funcs_all s2_track_funcs_all s2_begin s2_func_call s1_func_drop s1_func_stats s2_commit s2_ff s1_func_stats s2_func_stats
+step s1_track_funcs_all: SET track_functions = 'all';
+step s2_track_funcs_all: SET track_functions = 'all';
+step s2_begin: BEGIN;
+step s2_func_call: SELECT test_stat_func()
+test_stat_func
+--------------
+              
+(1 row)
+
+step s1_func_drop: DROP FUNCTION test_stat_func();
+step s1_func_stats: 
+    SELECT
+        tso.name,
+        pg_stat_get_function_calls(tso.oid),
+        pg_stat_get_function_total_time(tso.oid) > 0 total_above_zero,
+        pg_stat_get_function_self_time(tso.oid) > 0 self_above_zero
+    FROM test_stat_oid AS tso
+    WHERE tso.name = 'test_stat_func'
+
+name          |pg_stat_get_function_calls|total_above_zero|self_above_zero
+--------------+--------------------------+----------------+---------------
+test_stat_func|                          |                |               
+(1 row)
+
+step s2_commit: COMMIT;
+step s2_ff: SELECT pg_stat_force_next_flush();
+pg_stat_force_next_flush
+------------------------
+                        
+(1 row)
+
+step s1_func_stats: 
+    SELECT
+        tso.name,
+        pg_stat_get_function_calls(tso.oid),
+        pg_stat_get_function_total_time(tso.oid) > 0 total_above_zero,
+        pg_stat_get_function_self_time(tso.oid) > 0 self_above_zero
+    FROM test_stat_oid AS tso
+    WHERE tso.name = 'test_stat_func'
+
+name          |pg_stat_get_function_calls|total_above_zero|self_above_zero
+--------------+--------------------------+----------------+---------------
+test_stat_func|                          |                |               
+(1 row)
+
+step s2_func_stats: 
+    SELECT
+        tso.name,
+        pg_stat_get_function_calls(tso.oid),
+        pg_stat_get_function_total_time(tso.oid) > 0 total_above_zero,
+        pg_stat_get_function_self_time(tso.oid) > 0 self_above_zero
+    FROM test_stat_oid AS tso
+    WHERE tso.name = 'test_stat_func'
+
+name          |pg_stat_get_function_calls|total_above_zero|self_above_zero
+--------------+--------------------------+----------------+---------------
+test_stat_func|                          |                |               
+(1 row)
+
+
+starting permutation: s1_track_funcs_all s2_track_funcs_all s1_func_call s2_begin s2_func_call s1_func_drop s2_func_call s2_commit s2_ff s1_func_stats s2_func_stats
+step s1_track_funcs_all: SET track_functions = 'all';
+step s2_track_funcs_all: SET track_functions = 'all';
+step s1_func_call: SELECT test_stat_func();
+test_stat_func
+--------------
+              
+(1 row)
+
+step s2_begin: BEGIN;
+step s2_func_call: SELECT test_stat_func()
+test_stat_func
+--------------
+              
+(1 row)
+
+step s1_func_drop: DROP FUNCTION test_stat_func();
+step s2_func_call: SELECT test_stat_func()
+test_stat_func
+--------------
+              
+(1 row)
+
+step s2_commit: COMMIT;
+step s2_ff: SELECT pg_stat_force_next_flush();
+pg_stat_force_next_flush
+------------------------
+                        
+(1 row)
+
+step s1_func_stats: 
+    SELECT
+        tso.name,
+        pg_stat_get_function_calls(tso.oid),
+        pg_stat_get_function_total_time(tso.oid) > 0 total_above_zero,
+        pg_stat_get_function_self_time(tso.oid) > 0 self_above_zero
+    FROM test_stat_oid AS tso
+    WHERE tso.name = 'test_stat_func'
+
+name          |pg_stat_get_function_calls|total_above_zero|self_above_zero
+--------------+--------------------------+----------------+---------------
+test_stat_func|                          |                |               
+(1 row)
+
+step s2_func_stats: 
+    SELECT
+        tso.name,
+        pg_stat_get_function_calls(tso.oid),
+        pg_stat_get_function_total_time(tso.oid) > 0 total_above_zero,
+        pg_stat_get_function_self_time(tso.oid) > 0 self_above_zero
+    FROM test_stat_oid AS tso
+    WHERE tso.name = 'test_stat_func'
+
+name          |pg_stat_get_function_calls|total_above_zero|self_above_zero
+--------------+--------------------------+----------------+---------------
+test_stat_func|                          |                |               
+(1 row)
+
+
+starting permutation: s1_track_funcs_all s2_track_funcs_none s1_func_call s2_begin s2_func_call s1_func_drop s2_track_funcs_all s2_func_call s2_commit s2_ff s1_func_stats s2_func_stats
+step s1_track_funcs_all: SET track_functions = 'all';
+step s2_track_funcs_none: SET track_functions = 'none';
+step s1_func_call: SELECT test_stat_func();
+test_stat_func
+--------------
+              
+(1 row)
+
+step s2_begin: BEGIN;
+step s2_func_call: SELECT test_stat_func()
+test_stat_func
+--------------
+              
+(1 row)
+
+step s1_func_drop: DROP FUNCTION test_stat_func();
+step s2_track_funcs_all: SET track_functions = 'all';
+step s2_func_call: SELECT test_stat_func()
+test_stat_func
+--------------
+              
+(1 row)
+
+step s2_commit: COMMIT;
+step s2_ff: SELECT pg_stat_force_next_flush();
+pg_stat_force_next_flush
+------------------------
+                        
+(1 row)
+
+step s1_func_stats: 
+    SELECT
+        tso.name,
+        pg_stat_get_function_calls(tso.oid),
+        pg_stat_get_function_total_time(tso.oid) > 0 total_above_zero,
+        pg_stat_get_function_self_time(tso.oid) > 0 self_above_zero
+    FROM test_stat_oid AS tso
+    WHERE tso.name = 'test_stat_func'
+
+name          |pg_stat_get_function_calls|total_above_zero|self_above_zero
+--------------+--------------------------+----------------+---------------
+test_stat_func|                         1|t               |t              
+(1 row)
+
+step s2_func_stats: 
+    SELECT
+        tso.name,
+        pg_stat_get_function_calls(tso.oid),
+        pg_stat_get_function_total_time(tso.oid) > 0 total_above_zero,
+        pg_stat_get_function_self_time(tso.oid) > 0 self_above_zero
+    FROM test_stat_oid AS tso
+    WHERE tso.name = 'test_stat_func'
+
+name          |pg_stat_get_function_calls|total_above_zero|self_above_zero
+--------------+--------------------------+----------------+---------------
+test_stat_func|                         1|t               |t              
+(1 row)
+
+
+starting permutation: s1_track_funcs_all s2_track_funcs_all s1_func_call s2_func_call s2_func_call2 s1_ff s2_ff s1_func_stats s2_func_call s2_func_call2 s2_ff s1_func_stats s1_func_stats2 s1_func_stats s1_func_stats_reset s1_func_stats s1_func_stats2 s1_func_stats
+step s1_track_funcs_all: SET track_functions = 'all';
+step s2_track_funcs_all: SET track_functions = 'all';
+step s1_func_call: SELECT test_stat_func();
+test_stat_func
+--------------
+              
+(1 row)
+
+step s2_func_call: SELECT test_stat_func()
+test_stat_func
+--------------
+              
+(1 row)
+
+step s2_func_call2: SELECT test_stat_func2()
+test_stat_func2
+---------------
+               
+(1 row)
+
+step s1_ff: SELECT pg_stat_force_next_flush();
+pg_stat_force_next_flush
+------------------------
+                        
+(1 row)
+
+step s2_ff: SELECT pg_stat_force_next_flush();
+pg_stat_force_next_flush
+------------------------
+                        
+(1 row)
+
+step s1_func_stats: 
+    SELECT
+        tso.name,
+        pg_stat_get_function_calls(tso.oid),
+        pg_stat_get_function_total_time(tso.oid) > 0 total_above_zero,
+        pg_stat_get_function_self_time(tso.oid) > 0 self_above_zero
+    FROM test_stat_oid AS tso
+    WHERE tso.name = 'test_stat_func'
+
+name          |pg_stat_get_function_calls|total_above_zero|self_above_zero
+--------------+--------------------------+----------------+---------------
+test_stat_func|                         2|t               |t              
+(1 row)
+
+step s2_func_call: SELECT test_stat_func()
+test_stat_func
+--------------
+              
+(1 row)
+
+step s2_func_call2: SELECT test_stat_func2()
+test_stat_func2
+---------------
+               
+(1 row)
+
+step s2_ff: SELECT pg_stat_force_next_flush();
+pg_stat_force_next_flush
+------------------------
+                        
+(1 row)
+
+step s1_func_stats: 
+    SELECT
+        tso.name,
+        pg_stat_get_function_calls(tso.oid),
+        pg_stat_get_function_total_time(tso.oid) > 0 total_above_zero,
+        pg_stat_get_function_self_time(tso.oid) > 0 self_above_zero
+    FROM test_stat_oid AS tso
+    WHERE tso.name = 'test_stat_func'
+
+name          |pg_stat_get_function_calls|total_above_zero|self_above_zero
+--------------+--------------------------+----------------+---------------
+test_stat_func|                         3|t               |t              
+(1 row)
+
+step s1_func_stats2: 
+    SELECT
+        tso.name,
+        pg_stat_get_function_calls(tso.oid),
+        pg_stat_get_function_total_time(tso.oid) > 0 total_above_zero,
+        pg_stat_get_function_self_time(tso.oid) > 0 self_above_zero
+    FROM test_stat_oid AS tso
+    WHERE tso.name = 'test_stat_func2'
+
+name           |pg_stat_get_function_calls|total_above_zero|self_above_zero
+---------------+--------------------------+----------------+---------------
+test_stat_func2|                         2|t               |t              
+(1 row)
+
+step s1_func_stats: 
+    SELECT
+        tso.name,
+        pg_stat_get_function_calls(tso.oid),
+        pg_stat_get_function_total_time(tso.oid) > 0 total_above_zero,
+        pg_stat_get_function_self_time(tso.oid) > 0 self_above_zero
+    FROM test_stat_oid AS tso
+    WHERE tso.name = 'test_stat_func'
+
+name          |pg_stat_get_function_calls|total_above_zero|self_above_zero
+--------------+--------------------------+----------------+---------------
+test_stat_func|                         3|t               |t              
+(1 row)
+
+step s1_func_stats_reset: SELECT pg_stat_reset_single_function_counters('test_stat_func'::regproc);
+pg_stat_reset_single_function_counters
+--------------------------------------
+                                      
+(1 row)
+
+step s1_func_stats: 
+    SELECT
+        tso.name,
+        pg_stat_get_function_calls(tso.oid),
+        pg_stat_get_function_total_time(tso.oid) > 0 total_above_zero,
+        pg_stat_get_function_self_time(tso.oid) > 0 self_above_zero
+    FROM test_stat_oid AS tso
+    WHERE tso.name = 'test_stat_func'
+
+name          |pg_stat_get_function_calls|total_above_zero|self_above_zero
+--------------+--------------------------+----------------+---------------
+test_stat_func|                         0|f               |f              
+(1 row)
+
+step s1_func_stats2: 
+    SELECT
+        tso.name,
+        pg_stat_get_function_calls(tso.oid),
+        pg_stat_get_function_total_time(tso.oid) > 0 total_above_zero,
+        pg_stat_get_function_self_time(tso.oid) > 0 self_above_zero
+    FROM test_stat_oid AS tso
+    WHERE tso.name = 'test_stat_func2'
+
+name           |pg_stat_get_function_calls|total_above_zero|self_above_zero
+---------------+--------------------------+----------------+---------------
+test_stat_func2|                         2|t               |t              
+(1 row)
+
+step s1_func_stats: 
+    SELECT
+        tso.name,
+        pg_stat_get_function_calls(tso.oid),
+        pg_stat_get_function_total_time(tso.oid) > 0 total_above_zero,
+        pg_stat_get_function_self_time(tso.oid) > 0 self_above_zero
+    FROM test_stat_oid AS tso
+    WHERE tso.name = 'test_stat_func'
+
+name          |pg_stat_get_function_calls|total_above_zero|self_above_zero
+--------------+--------------------------+----------------+---------------
+test_stat_func|                         0|f               |f              
+(1 row)
+
+
+starting permutation: s1_track_funcs_all s2_track_funcs_all s1_func_call s2_func_call s2_func_call2 s1_ff s2_ff s1_func_stats s1_func_stats2 s1_func_stats s1_reset s1_func_stats s1_func_stats2 s1_func_stats
+step s1_track_funcs_all: SET track_functions = 'all';
+step s2_track_funcs_all: SET track_functions = 'all';
+step s1_func_call: SELECT test_stat_func();
+test_stat_func
+--------------
+              
+(1 row)
+
+step s2_func_call: SELECT test_stat_func()
+test_stat_func
+--------------
+              
+(1 row)
+
+step s2_func_call2: SELECT test_stat_func2()
+test_stat_func2
+---------------
+               
+(1 row)
+
+step s1_ff: SELECT pg_stat_force_next_flush();
+pg_stat_force_next_flush
+------------------------
+                        
+(1 row)
+
+step s2_ff: SELECT pg_stat_force_next_flush();
+pg_stat_force_next_flush
+------------------------
+                        
+(1 row)
+
+step s1_func_stats: 
+    SELECT
+        tso.name,
+        pg_stat_get_function_calls(tso.oid),
+        pg_stat_get_function_total_time(tso.oid) > 0 total_above_zero,
+        pg_stat_get_function_self_time(tso.oid) > 0 self_above_zero
+    FROM test_stat_oid AS tso
+    WHERE tso.name = 'test_stat_func'
+
+name          |pg_stat_get_function_calls|total_above_zero|self_above_zero
+--------------+--------------------------+----------------+---------------
+test_stat_func|                         2|t               |t              
+(1 row)
+
+step s1_func_stats2: 
+    SELECT
+        tso.name,
+        pg_stat_get_function_calls(tso.oid),
+        pg_stat_get_function_total_time(tso.oid) > 0 total_above_zero,
+        pg_stat_get_function_self_time(tso.oid) > 0 self_above_zero
+    FROM test_stat_oid AS tso
+    WHERE tso.name = 'test_stat_func2'
+
+name           |pg_stat_get_function_calls|total_above_zero|self_above_zero
+---------------+--------------------------+----------------+---------------
+test_stat_func2|                         1|t               |t              
+(1 row)
+
+step s1_func_stats: 
+    SELECT
+        tso.name,
+        pg_stat_get_function_calls(tso.oid),
+        pg_stat_get_function_total_time(tso.oid) > 0 total_above_zero,
+        pg_stat_get_function_self_time(tso.oid) > 0 self_above_zero
+    FROM test_stat_oid AS tso
+    WHERE tso.name = 'test_stat_func'
+
+name          |pg_stat_get_function_calls|total_above_zero|self_above_zero
+--------------+--------------------------+----------------+---------------
+test_stat_func|                         2|t               |t              
+(1 row)
+
+step s1_reset: SELECT pg_stat_reset();
+pg_stat_reset
+-------------
+             
+(1 row)
+
+step s1_func_stats: 
+    SELECT
+        tso.name,
+        pg_stat_get_function_calls(tso.oid),
+        pg_stat_get_function_total_time(tso.oid) > 0 total_above_zero,
+        pg_stat_get_function_self_time(tso.oid) > 0 self_above_zero
+    FROM test_stat_oid AS tso
+    WHERE tso.name = 'test_stat_func'
+
+name          |pg_stat_get_function_calls|total_above_zero|self_above_zero
+--------------+--------------------------+----------------+---------------
+test_stat_func|                         0|f               |f              
+(1 row)
+
+step s1_func_stats2: 
+    SELECT
+        tso.name,
+        pg_stat_get_function_calls(tso.oid),
+        pg_stat_get_function_total_time(tso.oid) > 0 total_above_zero,
+        pg_stat_get_function_self_time(tso.oid) > 0 self_above_zero
+    FROM test_stat_oid AS tso
+    WHERE tso.name = 'test_stat_func2'
+
+name           |pg_stat_get_function_calls|total_above_zero|self_above_zero
+---------------+--------------------------+----------------+---------------
+test_stat_func2|                         0|f               |f              
+(1 row)
+
+step s1_func_stats: 
+    SELECT
+        tso.name,
+        pg_stat_get_function_calls(tso.oid),
+        pg_stat_get_function_total_time(tso.oid) > 0 total_above_zero,
+        pg_stat_get_function_self_time(tso.oid) > 0 self_above_zero
+    FROM test_stat_oid AS tso
+    WHERE tso.name = 'test_stat_func'
+
+name          |pg_stat_get_function_calls|total_above_zero|self_above_zero
+--------------+--------------------------+----------------+---------------
+test_stat_func|                         0|f               |f              
+(1 row)
+
+
+starting permutation: s1_track_funcs_all s1_fetch_consistency_none s1_func_call s1_ff s1_func_stats
+step s1_track_funcs_all: SET track_functions = 'all';
+step s1_fetch_consistency_none: SET stats_fetch_consistency = 'none';
+step s1_func_call: SELECT test_stat_func();
+test_stat_func
+--------------
+              
+(1 row)
+
+step s1_ff: SELECT pg_stat_force_next_flush();
+pg_stat_force_next_flush
+------------------------
+                        
+(1 row)
+
+step s1_func_stats: 
+    SELECT
+        tso.name,
+        pg_stat_get_function_calls(tso.oid),
+        pg_stat_get_function_total_time(tso.oid) > 0 total_above_zero,
+        pg_stat_get_function_self_time(tso.oid) > 0 self_above_zero
+    FROM test_stat_oid AS tso
+    WHERE tso.name = 'test_stat_func'
+
+name          |pg_stat_get_function_calls|total_above_zero|self_above_zero
+--------------+--------------------------+----------------+---------------
+test_stat_func|                         1|t               |t              
+(1 row)
+
+
+starting permutation: s1_track_funcs_all s1_fetch_consistency_cache s1_func_call s1_ff s1_func_stats
+step s1_track_funcs_all: SET track_functions = 'all';
+step s1_fetch_consistency_cache: SET stats_fetch_consistency = 'cache';
+step s1_func_call: SELECT test_stat_func();
+test_stat_func
+--------------
+              
+(1 row)
+
+step s1_ff: SELECT pg_stat_force_next_flush();
+pg_stat_force_next_flush
+------------------------
+                        
+(1 row)
+
+step s1_func_stats: 
+    SELECT
+        tso.name,
+        pg_stat_get_function_calls(tso.oid),
+        pg_stat_get_function_total_time(tso.oid) > 0 total_above_zero,
+        pg_stat_get_function_self_time(tso.oid) > 0 self_above_zero
+    FROM test_stat_oid AS tso
+    WHERE tso.name = 'test_stat_func'
+
+name          |pg_stat_get_function_calls|total_above_zero|self_above_zero
+--------------+--------------------------+----------------+---------------
+test_stat_func|                         1|t               |t              
+(1 row)
+
+
+starting permutation: s1_track_funcs_all s1_fetch_consistency_snapshot s1_func_call s1_ff s1_func_stats
+step s1_track_funcs_all: SET track_functions = 'all';
+step s1_fetch_consistency_snapshot: SET stats_fetch_consistency = 'snapshot';
+step s1_func_call: SELECT test_stat_func();
+test_stat_func
+--------------
+              
+(1 row)
+
+step s1_ff: SELECT pg_stat_force_next_flush();
+pg_stat_force_next_flush
+------------------------
+                        
+(1 row)
+
+step s1_func_stats: 
+    SELECT
+        tso.name,
+        pg_stat_get_function_calls(tso.oid),
+        pg_stat_get_function_total_time(tso.oid) > 0 total_above_zero,
+        pg_stat_get_function_self_time(tso.oid) > 0 self_above_zero
+    FROM test_stat_oid AS tso
+    WHERE tso.name = 'test_stat_func'
+
+name          |pg_stat_get_function_calls|total_above_zero|self_above_zero
+--------------+--------------------------+----------------+---------------
+test_stat_func|                         1|t               |t              
+(1 row)
+
+
+starting permutation: s1_track_funcs_all s2_track_funcs_all s1_fetch_consistency_none s2_func_call s2_ff s1_begin s1_func_stats s2_func_call s2_ff s1_func_stats s1_commit
+step s1_track_funcs_all: SET track_functions = 'all';
+step s2_track_funcs_all: SET track_functions = 'all';
+step s1_fetch_consistency_none: SET stats_fetch_consistency = 'none';
+step s2_func_call: SELECT test_stat_func()
+test_stat_func
+--------------
+              
+(1 row)
+
+step s2_ff: SELECT pg_stat_force_next_flush();
+pg_stat_force_next_flush
+------------------------
+                        
+(1 row)
+
+step s1_begin: BEGIN;
+step s1_func_stats: 
+    SELECT
+        tso.name,
+        pg_stat_get_function_calls(tso.oid),
+        pg_stat_get_function_total_time(tso.oid) > 0 total_above_zero,
+        pg_stat_get_function_self_time(tso.oid) > 0 self_above_zero
+    FROM test_stat_oid AS tso
+    WHERE tso.name = 'test_stat_func'
+
+name          |pg_stat_get_function_calls|total_above_zero|self_above_zero
+--------------+--------------------------+----------------+---------------
+test_stat_func|                         1|t               |t              
+(1 row)
+
+step s2_func_call: SELECT test_stat_func()
+test_stat_func
+--------------
+              
+(1 row)
+
+step s2_ff: SELECT pg_stat_force_next_flush();
+pg_stat_force_next_flush
+------------------------
+                        
+(1 row)
+
+step s1_func_stats: 
+    SELECT
+        tso.name,
+        pg_stat_get_function_calls(tso.oid),
+        pg_stat_get_function_total_time(tso.oid) > 0 total_above_zero,
+        pg_stat_get_function_self_time(tso.oid) > 0 self_above_zero
+    FROM test_stat_oid AS tso
+    WHERE tso.name = 'test_stat_func'
+
+name          |pg_stat_get_function_calls|total_above_zero|self_above_zero
+--------------+--------------------------+----------------+---------------
+test_stat_func|                         2|t               |t              
+(1 row)
+
+step s1_commit: COMMIT;
+
+starting permutation: s1_track_funcs_all s2_track_funcs_all s1_fetch_consistency_cache s2_func_call s2_func_call2 s2_ff s1_begin s1_func_stats s2_func_call s2_func_call2 s2_ff s1_func_stats s1_func_stats2 s1_commit
+step s1_track_funcs_all: SET track_functions = 'all';
+step s2_track_funcs_all: SET track_functions = 'all';
+step s1_fetch_consistency_cache: SET stats_fetch_consistency = 'cache';
+step s2_func_call: SELECT test_stat_func()
+test_stat_func
+--------------
+              
+(1 row)
+
+step s2_func_call2: SELECT test_stat_func2()
+test_stat_func2
+---------------
+               
+(1 row)
+
+step s2_ff: SELECT pg_stat_force_next_flush();
+pg_stat_force_next_flush
+------------------------
+                        
+(1 row)
+
+step s1_begin: BEGIN;
+step s1_func_stats: 
+    SELECT
+        tso.name,
+        pg_stat_get_function_calls(tso.oid),
+        pg_stat_get_function_total_time(tso.oid) > 0 total_above_zero,
+        pg_stat_get_function_self_time(tso.oid) > 0 self_above_zero
+    FROM test_stat_oid AS tso
+    WHERE tso.name = 'test_stat_func'
+
+name          |pg_stat_get_function_calls|total_above_zero|self_above_zero
+--------------+--------------------------+----------------+---------------
+test_stat_func|                         1|t               |t              
+(1 row)
+
+step s2_func_call: SELECT test_stat_func()
+test_stat_func
+--------------
+              
+(1 row)
+
+step s2_func_call2: SELECT test_stat_func2()
+test_stat_func2
+---------------
+               
+(1 row)
+
+step s2_ff: SELECT pg_stat_force_next_flush();
+pg_stat_force_next_flush
+------------------------
+                        
+(1 row)
+
+step s1_func_stats: 
+    SELECT
+        tso.name,
+        pg_stat_get_function_calls(tso.oid),
+        pg_stat_get_function_total_time(tso.oid) > 0 total_above_zero,
+        pg_stat_get_function_self_time(tso.oid) > 0 self_above_zero
+    FROM test_stat_oid AS tso
+    WHERE tso.name = 'test_stat_func'
+
+name          |pg_stat_get_function_calls|total_above_zero|self_above_zero
+--------------+--------------------------+----------------+---------------
+test_stat_func|                         1|t               |t              
+(1 row)
+
+step s1_func_stats2: 
+    SELECT
+        tso.name,
+        pg_stat_get_function_calls(tso.oid),
+        pg_stat_get_function_total_time(tso.oid) > 0 total_above_zero,
+        pg_stat_get_function_self_time(tso.oid) > 0 self_above_zero
+    FROM test_stat_oid AS tso
+    WHERE tso.name = 'test_stat_func2'
+
+name           |pg_stat_get_function_calls|total_above_zero|self_above_zero
+---------------+--------------------------+----------------+---------------
+test_stat_func2|                         2|t               |t              
+(1 row)
+
+step s1_commit: COMMIT;
+
+starting permutation: s1_track_funcs_all s2_track_funcs_all s1_fetch_consistency_snapshot s2_func_call s2_func_call2 s2_ff s1_begin s1_func_stats s2_func_call s2_func_call2 s2_ff s1_func_stats s1_func_stats2 s1_commit
+step s1_track_funcs_all: SET track_functions = 'all';
+step s2_track_funcs_all: SET track_functions = 'all';
+step s1_fetch_consistency_snapshot: SET stats_fetch_consistency = 'snapshot';
+step s2_func_call: SELECT test_stat_func()
+test_stat_func
+--------------
+              
+(1 row)
+
+step s2_func_call2: SELECT test_stat_func2()
+test_stat_func2
+---------------
+               
+(1 row)
+
+step s2_ff: SELECT pg_stat_force_next_flush();
+pg_stat_force_next_flush
+------------------------
+                        
+(1 row)
+
+step s1_begin: BEGIN;
+step s1_func_stats: 
+    SELECT
+        tso.name,
+        pg_stat_get_function_calls(tso.oid),
+        pg_stat_get_function_total_time(tso.oid) > 0 total_above_zero,
+        pg_stat_get_function_self_time(tso.oid) > 0 self_above_zero
+    FROM test_stat_oid AS tso
+    WHERE tso.name = 'test_stat_func'
+
+name          |pg_stat_get_function_calls|total_above_zero|self_above_zero
+--------------+--------------------------+----------------+---------------
+test_stat_func|                         1|t               |t              
+(1 row)
+
+step s2_func_call: SELECT test_stat_func()
+test_stat_func
+--------------
+              
+(1 row)
+
+step s2_func_call2: SELECT test_stat_func2()
+test_stat_func2
+---------------
+               
+(1 row)
+
+step s2_ff: SELECT pg_stat_force_next_flush();
+pg_stat_force_next_flush
+------------------------
+                        
+(1 row)
+
+step s1_func_stats: 
+    SELECT
+        tso.name,
+        pg_stat_get_function_calls(tso.oid),
+        pg_stat_get_function_total_time(tso.oid) > 0 total_above_zero,
+        pg_stat_get_function_self_time(tso.oid) > 0 self_above_zero
+    FROM test_stat_oid AS tso
+    WHERE tso.name = 'test_stat_func'
+
+name          |pg_stat_get_function_calls|total_above_zero|self_above_zero
+--------------+--------------------------+----------------+---------------
+test_stat_func|                         1|t               |t              
+(1 row)
+
+step s1_func_stats2: 
+    SELECT
+        tso.name,
+        pg_stat_get_function_calls(tso.oid),
+        pg_stat_get_function_total_time(tso.oid) > 0 total_above_zero,
+        pg_stat_get_function_self_time(tso.oid) > 0 self_above_zero
+    FROM test_stat_oid AS tso
+    WHERE tso.name = 'test_stat_func2'
+
+name           |pg_stat_get_function_calls|total_above_zero|self_above_zero
+---------------+--------------------------+----------------+---------------
+test_stat_func2|                         1|t               |t              
+(1 row)
+
+step s1_commit: COMMIT;
+
+starting permutation: s1_track_funcs_all s2_track_funcs_all s1_begin s1_func_call s2_func_call s1_func_drop s2_func_call s2_ff s1_prepare_a s2_func_call s2_ff s1_func_call s1_ff s1_func_stats s1_commit_prepared_a s1_func_stats
+step s1_track_funcs_all: SET track_functions = 'all';
+step s2_track_funcs_all: SET track_functions = 'all';
+step s1_begin: BEGIN;
+step s1_func_call: SELECT test_stat_func();
+test_stat_func
+--------------
+              
+(1 row)
+
+step s2_func_call: SELECT test_stat_func()
+test_stat_func
+--------------
+              
+(1 row)
+
+step s1_func_drop: DROP FUNCTION test_stat_func();
+step s2_func_call: SELECT test_stat_func()
+test_stat_func
+--------------
+              
+(1 row)
+
+step s2_ff: SELECT pg_stat_force_next_flush();
+pg_stat_force_next_flush
+------------------------
+                        
+(1 row)
+
+step s1_prepare_a: PREPARE TRANSACTION 'a';
+step s2_func_call: SELECT test_stat_func()
+test_stat_func
+--------------
+              
+(1 row)
+
+step s2_ff: SELECT pg_stat_force_next_flush();
+pg_stat_force_next_flush
+------------------------
+                        
+(1 row)
+
+step s1_func_call: SELECT test_stat_func();
+test_stat_func
+--------------
+              
+(1 row)
+
+step s1_ff: SELECT pg_stat_force_next_flush();
+pg_stat_force_next_flush
+------------------------
+                        
+(1 row)
+
+step s1_func_stats: 
+    SELECT
+        tso.name,
+        pg_stat_get_function_calls(tso.oid),
+        pg_stat_get_function_total_time(tso.oid) > 0 total_above_zero,
+        pg_stat_get_function_self_time(tso.oid) > 0 self_above_zero
+    FROM test_stat_oid AS tso
+    WHERE tso.name = 'test_stat_func'
+
+name          |pg_stat_get_function_calls|total_above_zero|self_above_zero
+--------------+--------------------------+----------------+---------------
+test_stat_func|                         5|t               |t              
+(1 row)
+
+step s1_commit_prepared_a: COMMIT PREPARED 'a';
+step s1_func_stats: 
+    SELECT
+        tso.name,
+        pg_stat_get_function_calls(tso.oid),
+        pg_stat_get_function_total_time(tso.oid) > 0 total_above_zero,
+        pg_stat_get_function_self_time(tso.oid) > 0 self_above_zero
+    FROM test_stat_oid AS tso
+    WHERE tso.name = 'test_stat_func'
+
+name          |pg_stat_get_function_calls|total_above_zero|self_above_zero
+--------------+--------------------------+----------------+---------------
+test_stat_func|                          |                |               
+(1 row)
+
+
+starting permutation: s1_track_funcs_all s2_track_funcs_all s1_begin s1_func_call s2_func_call s1_func_drop s2_func_call s2_ff s1_prepare_a s2_func_call s2_ff s1_func_call s1_ff s1_func_stats s1_rollback_prepared_a s1_func_stats
+step s1_track_funcs_all: SET track_functions = 'all';
+step s2_track_funcs_all: SET track_functions = 'all';
+step s1_begin: BEGIN;
+step s1_func_call: SELECT test_stat_func();
+test_stat_func
+--------------
+              
+(1 row)
+
+step s2_func_call: SELECT test_stat_func()
+test_stat_func
+--------------
+              
+(1 row)
+
+step s1_func_drop: DROP FUNCTION test_stat_func();
+step s2_func_call: SELECT test_stat_func()
+test_stat_func
+--------------
+              
+(1 row)
+
+step s2_ff: SELECT pg_stat_force_next_flush();
+pg_stat_force_next_flush
+------------------------
+                        
+(1 row)
+
+step s1_prepare_a: PREPARE TRANSACTION 'a';
+step s2_func_call: SELECT test_stat_func()
+test_stat_func
+--------------
+              
+(1 row)
+
+step s2_ff: SELECT pg_stat_force_next_flush();
+pg_stat_force_next_flush
+------------------------
+                        
+(1 row)
+
+step s1_func_call: SELECT test_stat_func();
+test_stat_func
+--------------
+              
+(1 row)
+
+step s1_ff: SELECT pg_stat_force_next_flush();
+pg_stat_force_next_flush
+------------------------
+                        
+(1 row)
+
+step s1_func_stats: 
+    SELECT
+        tso.name,
+        pg_stat_get_function_calls(tso.oid),
+        pg_stat_get_function_total_time(tso.oid) > 0 total_above_zero,
+        pg_stat_get_function_self_time(tso.oid) > 0 self_above_zero
+    FROM test_stat_oid AS tso
+    WHERE tso.name = 'test_stat_func'
+
+name          |pg_stat_get_function_calls|total_above_zero|self_above_zero
+--------------+--------------------------+----------------+---------------
+test_stat_func|                         5|t               |t              
+(1 row)
+
+step s1_rollback_prepared_a: ROLLBACK PREPARED 'a';
+step s1_func_stats: 
+    SELECT
+        tso.name,
+        pg_stat_get_function_calls(tso.oid),
+        pg_stat_get_function_total_time(tso.oid) > 0 total_above_zero,
+        pg_stat_get_function_self_time(tso.oid) > 0 self_above_zero
+    FROM test_stat_oid AS tso
+    WHERE tso.name = 'test_stat_func'
+
+name          |pg_stat_get_function_calls|total_above_zero|self_above_zero
+--------------+--------------------------+----------------+---------------
+test_stat_func|                         5|t               |t              
+(1 row)
+
+
+starting permutation: s1_track_funcs_all s2_track_funcs_all s1_begin s1_func_call s2_func_call s1_func_drop s2_func_call s2_ff s1_prepare_a s2_func_call s2_ff s1_func_call s1_ff s1_func_stats s2_commit_prepared_a s1_func_stats
+step s1_track_funcs_all: SET track_functions = 'all';
+step s2_track_funcs_all: SET track_functions = 'all';
+step s1_begin: BEGIN;
+step s1_func_call: SELECT test_stat_func();
+test_stat_func
+--------------
+              
+(1 row)
+
+step s2_func_call: SELECT test_stat_func()
+test_stat_func
+--------------
+              
+(1 row)
+
+step s1_func_drop: DROP FUNCTION test_stat_func();
+step s2_func_call: SELECT test_stat_func()
+test_stat_func
+--------------
+              
+(1 row)
+
+step s2_ff: SELECT pg_stat_force_next_flush();
+pg_stat_force_next_flush
+------------------------
+                        
+(1 row)
+
+step s1_prepare_a: PREPARE TRANSACTION 'a';
+step s2_func_call: SELECT test_stat_func()
+test_stat_func
+--------------
+              
+(1 row)
+
+step s2_ff: SELECT pg_stat_force_next_flush();
+pg_stat_force_next_flush
+------------------------
+                        
+(1 row)
+
+step s1_func_call: SELECT test_stat_func();
+test_stat_func
+--------------
+              
+(1 row)
+
+step s1_ff: SELECT pg_stat_force_next_flush();
+pg_stat_force_next_flush
+------------------------
+                        
+(1 row)
+
+step s1_func_stats: 
+    SELECT
+        tso.name,
+        pg_stat_get_function_calls(tso.oid),
+        pg_stat_get_function_total_time(tso.oid) > 0 total_above_zero,
+        pg_stat_get_function_self_time(tso.oid) > 0 self_above_zero
+    FROM test_stat_oid AS tso
+    WHERE tso.name = 'test_stat_func'
+
+name          |pg_stat_get_function_calls|total_above_zero|self_above_zero
+--------------+--------------------------+----------------+---------------
+test_stat_func|                         5|t               |t              
+(1 row)
+
+step s2_commit_prepared_a: COMMIT PREPARED 'a';
+step s1_func_stats: 
+    SELECT
+        tso.name,
+        pg_stat_get_function_calls(tso.oid),
+        pg_stat_get_function_total_time(tso.oid) > 0 total_above_zero,
+        pg_stat_get_function_self_time(tso.oid) > 0 self_above_zero
+    FROM test_stat_oid AS tso
+    WHERE tso.name = 'test_stat_func'
+
+name          |pg_stat_get_function_calls|total_above_zero|self_above_zero
+--------------+--------------------------+----------------+---------------
+test_stat_func|                          |                |               
+(1 row)
+
+
+starting permutation: s1_track_funcs_all s2_track_funcs_all s1_begin s1_func_call s2_func_call s1_func_drop s2_func_call s2_ff s1_prepare_a s2_func_call s2_ff s1_func_call s1_ff s1_func_stats s2_rollback_prepared_a s1_func_stats
+step s1_track_funcs_all: SET track_functions = 'all';
+step s2_track_funcs_all: SET track_functions = 'all';
+step s1_begin: BEGIN;
+step s1_func_call: SELECT test_stat_func();
+test_stat_func
+--------------
+              
+(1 row)
+
+step s2_func_call: SELECT test_stat_func()
+test_stat_func
+--------------
+              
+(1 row)
+
+step s1_func_drop: DROP FUNCTION test_stat_func();
+step s2_func_call: SELECT test_stat_func()
+test_stat_func
+--------------
+              
+(1 row)
+
+step s2_ff: SELECT pg_stat_force_next_flush();
+pg_stat_force_next_flush
+------------------------
+                        
+(1 row)
+
+step s1_prepare_a: PREPARE TRANSACTION 'a';
+step s2_func_call: SELECT test_stat_func()
+test_stat_func
+--------------
+              
+(1 row)
+
+step s2_ff: SELECT pg_stat_force_next_flush();
+pg_stat_force_next_flush
+------------------------
+                        
+(1 row)
+
+step s1_func_call: SELECT test_stat_func();
+test_stat_func
+--------------
+              
+(1 row)
+
+step s1_ff: SELECT pg_stat_force_next_flush();
+pg_stat_force_next_flush
+------------------------
+                        
+(1 row)
+
+step s1_func_stats: 
+    SELECT
+        tso.name,
+        pg_stat_get_function_calls(tso.oid),
+        pg_stat_get_function_total_time(tso.oid) > 0 total_above_zero,
+        pg_stat_get_function_self_time(tso.oid) > 0 self_above_zero
+    FROM test_stat_oid AS tso
+    WHERE tso.name = 'test_stat_func'
+
+name          |pg_stat_get_function_calls|total_above_zero|self_above_zero
+--------------+--------------------------+----------------+---------------
+test_stat_func|                         5|t               |t              
+(1 row)
+
+step s2_rollback_prepared_a: ROLLBACK PREPARED 'a';
+step s1_func_stats: 
+    SELECT
+        tso.name,
+        pg_stat_get_function_calls(tso.oid),
+        pg_stat_get_function_total_time(tso.oid) > 0 total_above_zero,
+        pg_stat_get_function_self_time(tso.oid) > 0 self_above_zero
+    FROM test_stat_oid AS tso
+    WHERE tso.name = 'test_stat_func'
+
+name          |pg_stat_get_function_calls|total_above_zero|self_above_zero
+--------------+--------------------------+----------------+---------------
+test_stat_func|                         5|t               |t              
+(1 row)
+
diff --git a/src/test/isolation/isolation_schedule b/src/test/isolation/isolation_schedule
index 0dae483e827..d11b7d1ee44 100644
--- a/src/test/isolation/isolation_schedule
+++ b/src/test/isolation/isolation_schedule
@@ -1,3 +1,5 @@
+# FIXME: No reason for this to be first when merging
+test: stats
 test: read-only-anomaly
 test: read-only-anomaly-2
 test: read-only-anomaly-3
diff --git a/src/test/isolation/specs/stats.spec b/src/test/isolation/specs/stats.spec
new file mode 100644
index 00000000000..be993e9d391
--- /dev/null
+++ b/src/test/isolation/specs/stats.spec
@@ -0,0 +1,282 @@
+setup
+{
+    CREATE TABLE test_stat_oid(name text NOT NULL, oid oid);
+
+    CREATE TABLE test_stat_tab(id serial NOT NULL);
+    INSERT INTO test_stat_tab DEFAULT VALUES;
+    INSERT INTO test_stat_oid(name, oid) VALUES('test_stat_tab', 'test_stat_tab'::regclass);
+
+    CREATE FUNCTION test_stat_func() RETURNS VOID LANGUAGE plpgsql AS $$BEGIN END;$$;
+    INSERT INTO test_stat_oid(name, oid) VALUES('test_stat_func', 'test_stat_func'::regproc);
+
+    CREATE FUNCTION test_stat_func2() RETURNS VOID LANGUAGE plpgsql AS $$BEGIN END;$$;
+    INSERT INTO test_stat_oid(name, oid) VALUES('test_stat_func2', 'test_stat_func2'::regproc);
+}
+
+teardown
+{
+    DROP TABLE test_stat_oid;
+
+    DROP TABLE IF EXISTS test_stat_tab;
+    DROP FUNCTION IF EXISTS test_stat_func();
+    DROP FUNCTION IF EXISTS test_stat_func2();
+}
+
+session "s1"
+setup { SET stats_fetch_consistency = 'none'; }
+step "s1_track_funcs_all" { SET track_functions = 'all'; }
+step "s1_track_funcs_none" { SET track_functions = 'none'; }
+step "s1_fetch_consistency_none" { SET stats_fetch_consistency = 'none'; }
+step "s1_fetch_consistency_cache" { SET stats_fetch_consistency = 'cache'; }
+step "s1_fetch_consistency_snapshot" { SET stats_fetch_consistency = 'snapshot'; }
+step "s1_begin" { BEGIN; }
+step "s1_commit" { COMMIT; }
+step "s1_rollback" { ROLLBACK; }
+step "s1_prepare_a" { PREPARE TRANSACTION 'a'; }
+step "s1_commit_prepared_a" { COMMIT PREPARED 'a'; }
+step "s1_rollback_prepared_a" { ROLLBACK PREPARED 'a'; }
+step "s1_ff" { SELECT pg_stat_force_next_flush(); }
+step "s1_func_call" { SELECT test_stat_func(); }
+step "s1_func_drop" { DROP FUNCTION test_stat_func(); }
+step "s1_func_stats_reset" { SELECT pg_stat_reset_single_function_counters('test_stat_func'::regproc); }
+step "s1_reset" { SELECT pg_stat_reset(); }
+step "s1_func_stats" {
+    SELECT
+        tso.name,
+        pg_stat_get_function_calls(tso.oid),
+        pg_stat_get_function_total_time(tso.oid) > 0 total_above_zero,
+        pg_stat_get_function_self_time(tso.oid) > 0 self_above_zero
+    FROM test_stat_oid AS tso
+    WHERE tso.name = 'test_stat_func'
+}
+step "s1_func_stats2" {
+    SELECT
+        tso.name,
+        pg_stat_get_function_calls(tso.oid),
+        pg_stat_get_function_total_time(tso.oid) > 0 total_above_zero,
+        pg_stat_get_function_self_time(tso.oid) > 0 self_above_zero
+    FROM test_stat_oid AS tso
+    WHERE tso.name = 'test_stat_func2'
+}
+#step "s1_func_stats_debug" {SELECT * FROM pg_stat_user_functions;}
+
+session "s2"
+setup { SET stats_fetch_consistency = 'none'; }
+step "s2_track_funcs_all" { SET track_functions = 'all'; }
+step "s2_track_funcs_none" { SET track_functions = 'none'; }
+step "s2_begin" { BEGIN; }
+step "s2_commit" { COMMIT; }
+step "s2_commit_prepared_a" { COMMIT PREPARED 'a'; }
+step "s2_rollback_prepared_a" { ROLLBACK PREPARED 'a'; }
+step "s2_ff" { SELECT pg_stat_force_next_flush(); }
+step "s2_func_call" { SELECT test_stat_func() }
+step "s2_func_call2" { SELECT test_stat_func2() }
+step "s2_func_stats" {
+    SELECT
+        tso.name,
+        pg_stat_get_function_calls(tso.oid),
+        pg_stat_get_function_total_time(tso.oid) > 0 total_above_zero,
+        pg_stat_get_function_self_time(tso.oid) > 0 self_above_zero
+    FROM test_stat_oid AS tso
+    WHERE tso.name = 'test_stat_func'
+}
+
+
+######################
+# Function stats tests
+######################
+
+# check that stats are collected iff enabled
+permutation
+  "s1_track_funcs_none" "s1_func_stats" "s1_func_call" "s1_func_call" "s1_ff" "s1_func_stats"
+permutation
+  "s1_track_funcs_all" "s1_func_stats" "s1_func_call" "s1_func_call" "s1_ff" "s1_func_stats"
+
+# multiple function calls are accurately reported, across separate connections
+permutation
+  "s1_track_funcs_all" "s2_track_funcs_all" "s1_func_stats" "s2_func_stats"
+  "s1_func_call" "s2_func_call" "s1_func_call" "s2_func_call" "s2_func_call" "s1_ff" "s2_ff" "s1_func_stats" "s2_func_stats"
+permutation
+  "s1_track_funcs_all" "s2_track_funcs_all" "s1_func_stats" "s2_func_stats"
+  "s1_func_call" "s1_ff" "s2_func_call" "s2_func_call" "s2_ff" "s1_func_stats" "s2_func_stats"
+permutation
+  "s1_track_funcs_all" "s2_track_funcs_all" "s1_func_stats" "s2_func_stats"
+  "s1_begin" "s1_func_call" "s1_func_call" "s1_commit" "s1_ff" "s1_func_stats" "s2_func_stats"
+
+
+# Check interaction between dropping and stats reporting
+
+# dropping a table remove stats iff committed
+permutation
+  "s1_track_funcs_all" "s2_track_funcs_all" "s1_func_stats" "s2_func_stats"
+  "s1_begin" "s1_func_call" "s2_func_call" "s1_func_drop" "s2_func_call" "s2_ff" "s2_func_stats" "s1_commit" "s1_ff" "s1_func_stats" "s2_func_stats"
+permutation
+  "s1_track_funcs_all" "s2_track_funcs_all" "s1_func_stats" "s2_func_stats"
+  "s1_begin" "s1_func_call" "s2_func_call" "s1_func_drop" "s2_func_call" "s2_ff" "s2_func_stats" "s1_rollback" "s1_ff" "s1_func_stats" "s2_func_stats"
+
+# Verify that pending stats from before a drop do not lead to
+# "reviving" stats for a dropped object
+permutation
+  "s1_track_funcs_all" "s2_track_funcs_all"
+  "s2_func_call" "s2_ff" # this access increments refcount, preventing the shared entry from being dropped
+  "s2_begin" "s2_func_call" "s1_func_drop" "s1_func_stats" "s2_commit" "s2_ff" "s1_func_stats" "s2_func_stats"
+permutation
+  "s1_track_funcs_all" "s2_track_funcs_all"
+  "s2_begin" "s2_func_call" "s1_func_drop" "s1_func_stats" "s2_commit" "s2_ff" "s1_func_stats" "s2_func_stats"
+permutation
+  "s1_track_funcs_all" "s2_track_funcs_all"
+  "s1_func_call" "s2_begin" "s2_func_call" "s1_func_drop" "s2_func_call" "s2_commit" "s2_ff" "s1_func_stats" "s2_func_stats"
+
+# FIXME: this shows the bug that stats will be revived, because the
+# shared stats in s2 is only referenced *after* the DROP FUNCTION
+# committed. That's only possible because there is no locking (and
+# thus no stats invalidation) around function calls.
+permutation
+  "s1_track_funcs_all" "s2_track_funcs_none"
+  "s1_func_call" "s2_begin" "s2_func_call" "s1_func_drop" "s2_track_funcs_all" "s2_func_call" "s2_commit" "s2_ff" "s1_func_stats" "s2_func_stats"
+
+# test pg_stat_reset_single_function_counters
+permutation
+  "s1_track_funcs_all" "s2_track_funcs_all"
+  "s1_func_call"
+  "s2_func_call"
+  "s2_func_call2"
+  "s1_ff" "s2_ff"
+  "s1_func_stats"
+  "s2_func_call" "s2_func_call2" "s2_ff"
+  "s1_func_stats" "s1_func_stats2" "s1_func_stats"
+  "s1_func_stats_reset"
+  "s1_func_stats" "s1_func_stats2" "s1_func_stats"
+
+# test pg_stat_reset
+permutation
+  "s1_track_funcs_all" "s2_track_funcs_all"
+  "s1_func_call"
+  "s2_func_call"
+  "s2_func_call2"
+  "s1_ff" "s2_ff"
+  "s1_func_stats" "s1_func_stats2" "s1_func_stats"
+  "s1_reset"
+  "s1_func_stats" "s1_func_stats2" "s1_func_stats"
+
+
+# Check the different snapshot consistency models
+
+# First just some dead-trivial test verifying each model doesn't crash
+permutation
+  "s1_track_funcs_all" "s1_fetch_consistency_none" "s1_func_call" "s1_ff" "s1_func_stats"
+permutation
+  "s1_track_funcs_all" "s1_fetch_consistency_cache" "s1_func_call" "s1_ff" "s1_func_stats"
+permutation
+  "s1_track_funcs_all" "s1_fetch_consistency_snapshot" "s1_func_call" "s1_ff" "s1_func_stats"
+
+# with stats_fetch_consistency=none s1 should see flushed changes in s2, despite being in a transaction
+permutation
+  "s1_track_funcs_all" "s2_track_funcs_all"
+  "s1_fetch_consistency_none"
+  "s2_func_call" "s2_ff"
+  "s1_begin"
+  "s1_func_stats"
+  "s2_func_call" "s2_ff"
+  "s1_func_stats"
+  "s1_commit"
+
+# with stats_fetch_consistency=cache s1 should not see concurrent
+# changes to the same object after the first access, but a separate
+# object should show changes
+permutation
+  "s1_track_funcs_all" "s2_track_funcs_all"
+  "s1_fetch_consistency_cache"
+  "s2_func_call" "s2_func_call2" "s2_ff"
+  "s1_begin"
+  "s1_func_stats"
+  "s2_func_call" "s2_func_call2" "s2_ff"
+  "s1_func_stats" "s1_func_stats2"
+  "s1_commit"
+
+# with stats_fetch_consistency=snapshot s1 should not see any
+# concurrent changes after the first access
+permutation
+  "s1_track_funcs_all" "s2_track_funcs_all"
+  "s1_fetch_consistency_snapshot"
+  "s2_func_call" "s2_func_call2" "s2_ff"
+  "s1_begin"
+  "s1_func_stats"
+  "s2_func_call" "s2_func_call2" "s2_ff"
+  "s1_func_stats" "s1_func_stats2"
+  "s1_commit"
+
+
+# Check 2PC handling of stat drops
+
+# S1 prepared, S1 commits prepared
+permutation
+  "s1_track_funcs_all" "s2_track_funcs_all"
+  "s1_begin"
+  "s1_func_call"
+  "s2_func_call"
+  "s1_func_drop"
+  "s2_func_call"
+  "s2_ff"
+  "s1_prepare_a"
+  "s2_func_call"
+  "s2_ff"
+  "s1_func_call"
+  "s1_ff"
+  "s1_func_stats"
+  "s1_commit_prepared_a"
+  "s1_func_stats"
+
+# S1 prepared, S1 aborts prepared
+permutation
+  "s1_track_funcs_all" "s2_track_funcs_all"
+  "s1_begin"
+  "s1_func_call"
+  "s2_func_call"
+  "s1_func_drop"
+  "s2_func_call"
+  "s2_ff"
+  "s1_prepare_a"
+  "s2_func_call"
+  "s2_ff"
+  "s1_func_call"
+  "s1_ff"
+  "s1_func_stats"
+  "s1_rollback_prepared_a"
+  "s1_func_stats"
+
+# S1 prepares, S2 commits prepared
+permutation
+  "s1_track_funcs_all" "s2_track_funcs_all"
+  "s1_begin"
+  "s1_func_call"
+  "s2_func_call"
+  "s1_func_drop"
+  "s2_func_call"
+  "s2_ff"
+  "s1_prepare_a"
+  "s2_func_call"
+  "s2_ff"
+  "s1_func_call"
+  "s1_ff"
+  "s1_func_stats"
+  "s2_commit_prepared_a"
+  "s1_func_stats"
+
+# S1 prepared, S2 aborts prepared
+permutation
+  "s1_track_funcs_all" "s2_track_funcs_all"
+  "s1_begin"
+  "s1_func_call"
+  "s2_func_call"
+  "s1_func_drop"
+  "s2_func_call"
+  "s2_ff"
+  "s1_prepare_a"
+  "s2_func_call"
+  "s2_ff"
+  "s1_func_call"
+  "s1_ff"
+  "s1_func_stats"
+  "s2_rollback_prepared_a"
+  "s1_func_stats"
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index b9821cd8c76..99bb0a7e1f1 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -16,6 +16,8 @@ SET enable_seqscan TO on;
 SET enable_indexscan TO on;
 -- for the moment, we don't want index-only scans here
 SET enable_indexonlyscan TO off;
+-- not enabled by default, but we want to test it...
+SET track_functions TO 'all';
 -- save counters
 BEGIN;
 SET LOCAL stats_fetch_consistency = snapshot;
@@ -150,6 +152,108 @@ SELECT count(*) FROM tenk2 WHERE unique1 = 1;
 (1 row)
 
 RESET enable_bitmapscan;
+-- Check that stats for tables are dropped without needing to wait for
+-- vacuum. For that we need to access stats by oid after the DROP
+-- TABLE. Save it.
+CREATE TABLE drop_stats_test();
+INSERT INTO drop_stats_test DEFAULT VALUES;
+SELECT 'drop_stats_test'::regclass::oid AS drop_stats_test_oid \gset
+CREATE TABLE drop_stats_test_xact();
+INSERT INTO drop_stats_test_xact DEFAULT VALUES;
+SELECT 'drop_stats_test_xact'::regclass::oid AS drop_stats_test_xact_oid \gset
+CREATE TABLE drop_stats_test_subxact();
+INSERT INTO drop_stats_test_subxact DEFAULT VALUES;
+SELECT 'drop_stats_test_subxact'::regclass::oid AS drop_stats_test_subxact_oid \gset
+-- Basic testing for track_functions
+CREATE FUNCTION stats_test_func1() RETURNS VOID LANGUAGE plpgsql AS $$BEGIN END;$$;
+SELECT 'stats_test_func1()'::regprocedure::oid AS stats_test_func1_oid \gset
+CREATE FUNCTION stats_test_func2() RETURNS VOID LANGUAGE plpgsql AS $$BEGIN END;$$;
+SELECT 'stats_test_func2()'::regprocedure::oid AS stats_test_func2_oid \gset
+-- Basic test that stats are accumulated
+BEGIN;
+SELECT pg_stat_get_function_calls(:stats_test_func1_oid);
+ pg_stat_get_function_calls 
+----------------------------
+                           
+(1 row)
+
+SELECT pg_stat_get_xact_function_calls(:stats_test_func1_oid);
+ pg_stat_get_xact_function_calls 
+---------------------------------
+                                
+(1 row)
+
+SELECT stats_test_func1();
+ stats_test_func1 
+------------------
+ 
+(1 row)
+
+SELECT pg_stat_get_xact_function_calls(:stats_test_func1_oid);
+ pg_stat_get_xact_function_calls 
+---------------------------------
+                               1
+(1 row)
+
+SELECT stats_test_func1();
+ stats_test_func1 
+------------------
+ 
+(1 row)
+
+SELECT pg_stat_get_xact_function_calls(:stats_test_func1_oid);
+ pg_stat_get_xact_function_calls 
+---------------------------------
+                               2
+(1 row)
+
+SELECT pg_stat_get_function_calls(:stats_test_func1_oid);
+ pg_stat_get_function_calls 
+----------------------------
+                          0
+(1 row)
+
+COMMIT;
+-- Verify that function stats are not transactional (displayed after
+-- wait_for_stats() below)
+-- rolled back savepoint in committing transaction
+BEGIN;
+SELECT stats_test_func2();
+ stats_test_func2 
+------------------
+ 
+(1 row)
+
+SAVEPOINT foo;
+SELECT stats_test_func2();
+ stats_test_func2 
+------------------
+ 
+(1 row)
+
+ROLLBACK TO SAVEPOINT foo;
+SELECT pg_stat_get_xact_function_calls(:stats_test_func2_oid);
+ pg_stat_get_xact_function_calls 
+---------------------------------
+                               2
+(1 row)
+
+SELECT stats_test_func2();
+ stats_test_func2 
+------------------
+ 
+(1 row)
+
+COMMIT;
+-- rolled back transaction
+BEGIN;
+SELECT stats_test_func2();
+ stats_test_func2 
+------------------
+ 
+(1 row)
+
+ROLLBACK;
 -- We can't just call wait_for_stats() at this point, because we only
 -- transmit stats when the session goes idle, and we probably didn't
 -- transmit the last couple of counts yet thanks to the rate-limiting logic
@@ -207,6 +311,270 @@ FROM prevstats AS pr;
 (1 row)
 
 COMMIT;
+-- check stats are dropped (happens synchronously)
+SELECT pg_stat_get_live_tuples(:drop_stats_test_oid);
+ pg_stat_get_live_tuples 
+-------------------------
+                       1
+(1 row)
+
+DROP TABLE drop_stats_test;
+SELECT pg_stat_get_live_tuples(:drop_stats_test_oid);
+ pg_stat_get_live_tuples 
+-------------------------
+                       0
+(1 row)
+
+SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_oid);
+ pg_stat_get_xact_tuples_inserted 
+----------------------------------
+                                0
+(1 row)
+
+-- check that rollback protects against having stats dropped
+-- and that local modifcations don't pose a problem
+SELECT pg_stat_get_live_tuples(:drop_stats_test_xact_oid);
+ pg_stat_get_live_tuples 
+-------------------------
+                       1
+(1 row)
+
+SELECT pg_stat_get_tuples_inserted(:drop_stats_test_xact_oid);
+ pg_stat_get_tuples_inserted 
+-----------------------------
+                           1
+(1 row)
+
+SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_xact_oid);
+ pg_stat_get_xact_tuples_inserted 
+----------------------------------
+                                0
+(1 row)
+
+BEGIN;
+INSERT INTO drop_stats_test_xact DEFAULT VALUES;
+SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_xact_oid);
+ pg_stat_get_xact_tuples_inserted 
+----------------------------------
+                                1
+(1 row)
+
+DROP TABLE drop_stats_test_xact;
+SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_xact_oid);
+ pg_stat_get_xact_tuples_inserted 
+----------------------------------
+                                0
+(1 row)
+
+ROLLBACK;
+SELECT pg_stat_get_live_tuples(:drop_stats_test_xact_oid);
+ pg_stat_get_live_tuples 
+-------------------------
+                       1
+(1 row)
+
+SELECT pg_stat_get_tuples_inserted(:drop_stats_test_xact_oid);
+ pg_stat_get_tuples_inserted 
+-----------------------------
+                           1
+(1 row)
+
+-- transactional drop (Cannot use pg_stat_get_xact again, timing
+-- dependent whether already flushed)
+SELECT pg_stat_get_live_tuples(:drop_stats_test_xact_oid);
+ pg_stat_get_live_tuples 
+-------------------------
+                       1
+(1 row)
+
+SELECT pg_stat_get_tuples_inserted(:drop_stats_test_xact_oid);
+ pg_stat_get_tuples_inserted 
+-----------------------------
+                           1
+(1 row)
+
+BEGIN;
+INSERT INTO drop_stats_test_xact DEFAULT VALUES;
+DROP TABLE drop_stats_test_xact;
+COMMIT;
+SELECT pg_stat_get_live_tuples(:drop_stats_test_xact_oid);
+ pg_stat_get_live_tuples 
+-------------------------
+                       0
+(1 row)
+
+SELECT pg_stat_get_tuples_inserted(:drop_stats_test_xact_oid);
+ pg_stat_get_tuples_inserted 
+-----------------------------
+                           0
+(1 row)
+
+-- savepoint rollback (2 levels)
+SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
+ pg_stat_get_live_tuples 
+-------------------------
+                       1
+(1 row)
+
+BEGIN;
+INSERT INTO drop_stats_test_subxact DEFAULT VALUES;
+SAVEPOINT sp1;
+INSERT INTO drop_stats_test_subxact DEFAULT VALUES;
+SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_subxact_oid);
+ pg_stat_get_xact_tuples_inserted 
+----------------------------------
+                                2
+(1 row)
+
+SAVEPOINT sp2;
+DROP TABLE drop_stats_test_subxact;
+ROLLBACK TO SAVEPOINT sp2;
+SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_subxact_oid);
+ pg_stat_get_xact_tuples_inserted 
+----------------------------------
+                                2
+(1 row)
+
+COMMIT;
+SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
+ pg_stat_get_live_tuples 
+-------------------------
+                       1
+(1 row)
+
+-- savepoint rolback (1 level)
+SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
+ pg_stat_get_live_tuples 
+-------------------------
+                       1
+(1 row)
+
+BEGIN;
+SAVEPOINT sp1;
+DROP TABLE drop_stats_test_subxact;
+SAVEPOINT sp2;
+ROLLBACK TO SAVEPOINT sp1;
+COMMIT;
+SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
+ pg_stat_get_live_tuples 
+-------------------------
+                       1
+(1 row)
+
+-- and now actually drop
+SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
+ pg_stat_get_live_tuples 
+-------------------------
+                       1
+(1 row)
+
+BEGIN;
+SAVEPOINT sp1;
+DROP TABLE drop_stats_test_subxact;
+SAVEPOINT sp2;
+RELEASE SAVEPOINT sp1;
+COMMIT;
+SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
+ pg_stat_get_live_tuples 
+-------------------------
+                       0
+(1 row)
+
+-----
+-- continuation of track function tests
+-----
+-- check stats were collected
+SELECT funcname, calls FROM pg_stat_user_functions WHERE funcid = :stats_test_func1_oid;
+     funcname     | calls 
+------------------+-------
+ stats_test_func1 |     2
+(1 row)
+
+SELECT funcname, calls FROM pg_stat_user_functions WHERE funcid = :stats_test_func2_oid;
+     funcname     | calls 
+------------------+-------
+ stats_test_func2 |     4
+(1 row)
+
+-- check that a rolled back drop function stats leaves stats alive
+BEGIN;
+SELECT funcname, calls FROM pg_stat_user_functions WHERE funcid = :stats_test_func1_oid;
+     funcname     | calls 
+------------------+-------
+ stats_test_func1 |     2
+(1 row)
+
+DROP FUNCTION stats_test_func1();
+-- shouldn't be visible via view
+SELECT funcname, calls FROM pg_stat_user_functions WHERE funcid = :stats_test_func1_oid;
+ funcname | calls 
+----------+-------
+(0 rows)
+
+-- but still via oid access
+SELECT pg_stat_get_function_calls(:stats_test_func1_oid);
+ pg_stat_get_function_calls 
+----------------------------
+                          2
+(1 row)
+
+ROLLBACK;
+SELECT funcname, calls FROM pg_stat_user_functions WHERE funcid = :stats_test_func1_oid;
+     funcname     | calls 
+------------------+-------
+ stats_test_func1 |     2
+(1 row)
+
+SELECT pg_stat_get_function_calls(:stats_test_func1_oid);
+ pg_stat_get_function_calls 
+----------------------------
+                          2
+(1 row)
+
+-- check that function dropped in main transaction leaves no stats behind
+BEGIN;
+DROP FUNCTION stats_test_func1();
+COMMIT;
+SELECT funcname, calls FROM pg_stat_user_functions WHERE funcid = :stats_test_func1_oid;
+ funcname | calls 
+----------+-------
+(0 rows)
+
+SELECT pg_stat_get_function_calls(:stats_test_func1_oid);
+ pg_stat_get_function_calls 
+----------------------------
+                           
+(1 row)
+
+-- check that function dropped in a subtransaction leaves no stats behind
+BEGIN;
+SELECT stats_test_func2();
+ stats_test_func2 
+------------------
+ 
+(1 row)
+
+SAVEPOINT a;
+SELECT stats_test_func2();
+ stats_test_func2 
+------------------
+ 
+(1 row)
+
+SAVEPOINT b;
+DROP FUNCTION stats_test_func2();
+COMMIT;
+SELECT funcname, calls FROM pg_stat_user_functions WHERE funcid = :stats_test_func2_oid;
+ funcname | calls 
+----------+-------
+(0 rows)
+
+SELECT pg_stat_get_function_calls(:stats_test_func2_oid);
+ pg_stat_get_function_calls 
+----------------------------
+                           
+(1 row)
+
 DROP TABLE trunc_stats_test, trunc_stats_test1, trunc_stats_test2, trunc_stats_test3, trunc_stats_test4;
 DROP TABLE prevstats;
 -- test BRIN index doesn't block HOT update - we include this test here, as it
@@ -260,4 +628,16 @@ SELECT pg_stat_get_tuples_hot_updated('brin_hot'::regclass::oid);
 
 DROP TABLE brin_hot;
 DROP FUNCTION wait_for_hot_stats();
+\dt *stats_test*
+      List of relations
+ Schema | Name | Type | Owner 
+--------+------+------+-------
+(0 rows)
+
+\dv *stats_test*
+      List of relations
+ Schema | Name | Type | Owner 
+--------+------+------+-------
+(0 rows)
+
 -- End of Stats Test
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 9dfd44748e5..e5256ab33c7 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -13,6 +13,8 @@ SET enable_seqscan TO on;
 SET enable_indexscan TO on;
 -- for the moment, we don't want index-only scans here
 SET enable_indexonlyscan TO off;
+-- not enabled by default, but we want to test it...
+SET track_functions TO 'all';
 
 -- save counters
 BEGIN;
@@ -148,6 +150,58 @@ SET enable_bitmapscan TO off;
 SELECT count(*) FROM tenk2 WHERE unique1 = 1;
 RESET enable_bitmapscan;
 
+-- Check that stats for tables are dropped without needing to wait for
+-- vacuum. For that we need to access stats by oid after the DROP
+-- TABLE. Save it.
+CREATE TABLE drop_stats_test();
+INSERT INTO drop_stats_test DEFAULT VALUES;
+SELECT 'drop_stats_test'::regclass::oid AS drop_stats_test_oid \gset
+
+CREATE TABLE drop_stats_test_xact();
+INSERT INTO drop_stats_test_xact DEFAULT VALUES;
+SELECT 'drop_stats_test_xact'::regclass::oid AS drop_stats_test_xact_oid \gset
+
+CREATE TABLE drop_stats_test_subxact();
+INSERT INTO drop_stats_test_subxact DEFAULT VALUES;
+SELECT 'drop_stats_test_subxact'::regclass::oid AS drop_stats_test_subxact_oid \gset
+
+
+-- Basic testing for track_functions
+CREATE FUNCTION stats_test_func1() RETURNS VOID LANGUAGE plpgsql AS $$BEGIN END;$$;
+SELECT 'stats_test_func1()'::regprocedure::oid AS stats_test_func1_oid \gset
+CREATE FUNCTION stats_test_func2() RETURNS VOID LANGUAGE plpgsql AS $$BEGIN END;$$;
+SELECT 'stats_test_func2()'::regprocedure::oid AS stats_test_func2_oid \gset
+
+-- Basic test that stats are accumulated
+BEGIN;
+SELECT pg_stat_get_function_calls(:stats_test_func1_oid);
+SELECT pg_stat_get_xact_function_calls(:stats_test_func1_oid);
+SELECT stats_test_func1();
+SELECT pg_stat_get_xact_function_calls(:stats_test_func1_oid);
+SELECT stats_test_func1();
+SELECT pg_stat_get_xact_function_calls(:stats_test_func1_oid);
+SELECT pg_stat_get_function_calls(:stats_test_func1_oid);
+COMMIT;
+
+-- Verify that function stats are not transactional (displayed after
+-- wait_for_stats() below)
+
+-- rolled back savepoint in committing transaction
+BEGIN;
+SELECT stats_test_func2();
+SAVEPOINT foo;
+SELECT stats_test_func2();
+ROLLBACK TO SAVEPOINT foo;
+SELECT pg_stat_get_xact_function_calls(:stats_test_func2_oid);
+SELECT stats_test_func2();
+COMMIT;
+
+-- rolled back transaction
+BEGIN;
+SELECT stats_test_func2();
+ROLLBACK;
+
+
 -- We can't just call wait_for_stats() at this point, because we only
 -- transmit stats when the session goes idle, and we probably didn't
 -- transmit the last couple of counts yet thanks to the rate-limiting logic
@@ -161,6 +215,7 @@ SELECT wait_for_stats();
 
 -- check effects
 BEGIN;
+
 SET LOCAL stats_fetch_consistency = snapshot;
 
 SELECT relname, n_tup_ins, n_tup_upd, n_tup_del, n_live_tup, n_dead_tup
@@ -184,6 +239,110 @@ FROM prevstats AS pr;
 
 COMMIT;
 
+
+-- check stats are dropped (happens synchronously)
+SELECT pg_stat_get_live_tuples(:drop_stats_test_oid);
+DROP TABLE drop_stats_test;
+SELECT pg_stat_get_live_tuples(:drop_stats_test_oid);
+SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_oid);
+
+-- check that rollback protects against having stats dropped
+-- and that local modifcations don't pose a problem
+SELECT pg_stat_get_live_tuples(:drop_stats_test_xact_oid);
+SELECT pg_stat_get_tuples_inserted(:drop_stats_test_xact_oid);
+SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_xact_oid);
+BEGIN;
+INSERT INTO drop_stats_test_xact DEFAULT VALUES;
+SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_xact_oid);
+DROP TABLE drop_stats_test_xact;
+SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_xact_oid);
+ROLLBACK;
+SELECT pg_stat_get_live_tuples(:drop_stats_test_xact_oid);
+SELECT pg_stat_get_tuples_inserted(:drop_stats_test_xact_oid);
+
+-- transactional drop (Cannot use pg_stat_get_xact again, timing
+-- dependent whether already flushed)
+SELECT pg_stat_get_live_tuples(:drop_stats_test_xact_oid);
+SELECT pg_stat_get_tuples_inserted(:drop_stats_test_xact_oid);
+BEGIN;
+INSERT INTO drop_stats_test_xact DEFAULT VALUES;
+DROP TABLE drop_stats_test_xact;
+COMMIT;
+SELECT pg_stat_get_live_tuples(:drop_stats_test_xact_oid);
+SELECT pg_stat_get_tuples_inserted(:drop_stats_test_xact_oid);
+
+-- savepoint rollback (2 levels)
+SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
+BEGIN;
+INSERT INTO drop_stats_test_subxact DEFAULT VALUES;
+SAVEPOINT sp1;
+INSERT INTO drop_stats_test_subxact DEFAULT VALUES;
+SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_subxact_oid);
+SAVEPOINT sp2;
+DROP TABLE drop_stats_test_subxact;
+ROLLBACK TO SAVEPOINT sp2;
+SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_subxact_oid);
+COMMIT;
+SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
+
+-- savepoint rolback (1 level)
+SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
+BEGIN;
+SAVEPOINT sp1;
+DROP TABLE drop_stats_test_subxact;
+SAVEPOINT sp2;
+ROLLBACK TO SAVEPOINT sp1;
+COMMIT;
+SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
+
+-- and now actually drop
+SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
+BEGIN;
+SAVEPOINT sp1;
+DROP TABLE drop_stats_test_subxact;
+SAVEPOINT sp2;
+RELEASE SAVEPOINT sp1;
+COMMIT;
+SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
+
+-----
+-- continuation of track function tests
+-----
+
+-- check stats were collected
+SELECT funcname, calls FROM pg_stat_user_functions WHERE funcid = :stats_test_func1_oid;
+SELECT funcname, calls FROM pg_stat_user_functions WHERE funcid = :stats_test_func2_oid;
+
+-- check that a rolled back drop function stats leaves stats alive
+BEGIN;
+SELECT funcname, calls FROM pg_stat_user_functions WHERE funcid = :stats_test_func1_oid;
+DROP FUNCTION stats_test_func1();
+-- shouldn't be visible via view
+SELECT funcname, calls FROM pg_stat_user_functions WHERE funcid = :stats_test_func1_oid;
+-- but still via oid access
+SELECT pg_stat_get_function_calls(:stats_test_func1_oid);
+ROLLBACK;
+SELECT funcname, calls FROM pg_stat_user_functions WHERE funcid = :stats_test_func1_oid;
+SELECT pg_stat_get_function_calls(:stats_test_func1_oid);
+
+-- check that function dropped in main transaction leaves no stats behind
+BEGIN;
+DROP FUNCTION stats_test_func1();
+COMMIT;
+SELECT funcname, calls FROM pg_stat_user_functions WHERE funcid = :stats_test_func1_oid;
+SELECT pg_stat_get_function_calls(:stats_test_func1_oid);
+
+-- check that function dropped in a subtransaction leaves no stats behind
+BEGIN;
+SELECT stats_test_func2();
+SAVEPOINT a;
+SELECT stats_test_func2();
+SAVEPOINT b;
+DROP FUNCTION stats_test_func2();
+COMMIT;
+SELECT funcname, calls FROM pg_stat_user_functions WHERE funcid = :stats_test_func2_oid;
+SELECT pg_stat_get_function_calls(:stats_test_func2_oid);
+
 DROP TABLE trunc_stats_test, trunc_stats_test1, trunc_stats_test2, trunc_stats_test3, trunc_stats_test4;
 DROP TABLE prevstats;
 
@@ -236,4 +395,7 @@ DROP TABLE brin_hot;
 DROP FUNCTION wait_for_hot_stats();
 
 
+
+\dt *stats_test*
+\dv *stats_test*
 -- End of Stats Test
-- 
2.35.1.354.g715d08a9e5

