postgresql/src/test/regress/expected/stats.out
Michael Paquier d7744d50a5 Fix initialization of pg_stat_get_lastscan()
A NULL result should be reported when a stats timestamp is set to 0, but
c037471 missed that, leading to a confusing timestamp value after for
example a DML on a freshly-created relation with no scans done on it
yet.

This impacted the following attributes for two system views:
- pg_stat_all_tables.last_idx_scan
- pg_stat_all_tables.last_seq_scan
- pg_stat_all_indexes.last_idx_scan

Reported-by: Robert Treat
Analyzed-by: Peter Eisentraut
Author: Dave Page
Discussion: https://postgr.es/m/CABV9wwPzMfSaz3EfKXXDxKmMprbxwF5r6WPuxqA=5mzRUqfTGg@mail.gmail.com
2022-11-08 10:50:09 +09:00

1130 lines
30 KiB
Plaintext

--
-- Test cumulative stats system
--
-- Must be run after tenk2 has been created (by create_table),
-- populated (by create_misc) and indexed (by create_index).
--
-- conditio sine qua non
SHOW track_counts; -- must be on
track_counts
--------------
on
(1 row)
-- ensure that both seqscan and indexscan plans are allowed
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';
-- record dboid for later use
SELECT oid AS dboid from pg_database where datname = current_database() \gset
-- save counters
BEGIN;
SET LOCAL stats_fetch_consistency = snapshot;
CREATE TABLE prevstats AS
SELECT t.seq_scan, t.seq_tup_read, t.idx_scan, t.idx_tup_fetch,
(b.heap_blks_read + b.heap_blks_hit) AS heap_blks,
(b.idx_blks_read + b.idx_blks_hit) AS idx_blks,
pg_stat_get_snapshot_timestamp() as snap_ts
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';
COMMIT;
-- test effects of TRUNCATE on n_live_tup/n_dead_tup counters
CREATE TABLE trunc_stats_test(id serial);
CREATE TABLE trunc_stats_test1(id serial, stuff text);
CREATE TABLE trunc_stats_test2(id serial);
CREATE TABLE trunc_stats_test3(id serial, stuff text);
CREATE TABLE trunc_stats_test4(id serial);
-- check that n_live_tup is reset to 0 after truncate
INSERT INTO trunc_stats_test DEFAULT VALUES;
INSERT INTO trunc_stats_test DEFAULT VALUES;
INSERT INTO trunc_stats_test DEFAULT VALUES;
TRUNCATE trunc_stats_test;
-- test involving a truncate in a transaction; 4 ins but only 1 live
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
DELETE FROM trunc_stats_test1 WHERE id = 3;
BEGIN;
UPDATE trunc_stats_test1 SET id = id + 100;
TRUNCATE trunc_stats_test1;
INSERT INTO trunc_stats_test1 DEFAULT VALUES;
COMMIT;
-- use a savepoint: 1 insert, 1 live
BEGIN;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
SAVEPOINT p1;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
TRUNCATE trunc_stats_test2;
INSERT INTO trunc_stats_test2 DEFAULT VALUES;
RELEASE SAVEPOINT p1;
COMMIT;
-- rollback a savepoint: this should count 4 inserts and have 2
-- live tuples after commit (and 2 dead ones due to aborted subxact)
BEGIN;
INSERT INTO trunc_stats_test3 DEFAULT VALUES;
INSERT INTO trunc_stats_test3 DEFAULT VALUES;
SAVEPOINT p1;
INSERT INTO trunc_stats_test3 DEFAULT VALUES;
INSERT INTO trunc_stats_test3 DEFAULT VALUES;
TRUNCATE trunc_stats_test3;
INSERT INTO trunc_stats_test3 DEFAULT VALUES;
ROLLBACK TO SAVEPOINT p1;
COMMIT;
-- rollback a truncate: this should count 2 inserts and produce 2 dead tuples
BEGIN;
INSERT INTO trunc_stats_test4 DEFAULT VALUES;
INSERT INTO trunc_stats_test4 DEFAULT VALUES;
TRUNCATE trunc_stats_test4;
INSERT INTO trunc_stats_test4 DEFAULT VALUES;
ROLLBACK;
-- do a seqscan
SELECT count(*) FROM tenk2;
count
-------
10000
(1 row)
-- do an indexscan
-- make sure it is not a bitmap scan, which might skip fetching heap tuples
SET enable_bitmapscan TO off;
SELECT count(*) FROM tenk2 WHERE unique1 = 1;
count
-------
1
(1 row)
RESET enable_bitmapscan;
-- ensure pending stats are flushed
SELECT pg_stat_force_next_flush();
pg_stat_force_next_flush
--------------------------
(1 row)
-- 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
FROM pg_stat_user_tables
WHERE relname like 'trunc_stats_test%' order by relname;
relname | n_tup_ins | n_tup_upd | n_tup_del | n_live_tup | n_dead_tup
-------------------+-----------+-----------+-----------+------------+------------
trunc_stats_test | 3 | 0 | 0 | 0 | 0
trunc_stats_test1 | 4 | 2 | 1 | 1 | 0
trunc_stats_test2 | 1 | 0 | 0 | 1 | 0
trunc_stats_test3 | 4 | 0 | 0 | 2 | 2
trunc_stats_test4 | 2 | 0 | 0 | 0 | 2
(5 rows)
SELECT st.seq_scan >= pr.seq_scan + 1,
st.seq_tup_read >= pr.seq_tup_read + cl.reltuples,
st.idx_scan >= pr.idx_scan + 1,
st.idx_tup_fetch >= pr.idx_tup_fetch + 1
FROM pg_stat_user_tables AS st, pg_class AS cl, prevstats AS pr
WHERE st.relname='tenk2' AND cl.relname='tenk2';
?column? | ?column? | ?column? | ?column?
----------+----------+----------+----------
t | t | t | t
(1 row)
SELECT st.heap_blks_read + st.heap_blks_hit >= pr.heap_blks + cl.relpages,
st.idx_blks_read + st.idx_blks_hit >= pr.idx_blks + 1
FROM pg_statio_user_tables AS st, pg_class AS cl, prevstats AS pr
WHERE st.relname='tenk2' AND cl.relname='tenk2';
?column? | ?column?
----------+----------
t | t
(1 row)
SELECT pr.snap_ts < pg_stat_get_snapshot_timestamp() as snapshot_newer
FROM prevstats AS pr;
snapshot_newer
----------------
t
(1 row)
COMMIT;
----
-- Basic tests 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
-- test that stats are accumulated
BEGIN;
SET LOCAL stats_fetch_consistency = none;
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
-- 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;
SELECT pg_stat_force_next_flush();
pg_stat_force_next_flush
--------------------------
(1 row)
-- check collected stats
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)
-- Check that stats for relations are dropped. For that we need to access stats
-- by oid after the DROP TABLE. Save oids.
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
SELECT pg_stat_force_next_flush();
pg_stat_force_next_flush
--------------------------
(1 row)
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
-- modifications 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_force_next_flush();
pg_stat_force_next_flush
--------------------------
(1 row)
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
-----------------------------
2
(1 row)
-- transactional drop
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
-----------------------------
2
(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)
COMMIT;
SELECT pg_stat_force_next_flush();
pg_stat_force_next_flush
--------------------------
(1 row)
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_force_next_flush();
pg_stat_force_next_flush
--------------------------
(1 row)
SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
pg_stat_get_live_tuples
-------------------------
3
(1 row)
-- savepoint rolback (1 level)
SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
pg_stat_get_live_tuples
-------------------------
3
(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
-------------------------
3
(1 row)
-- and now actually drop
SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
pg_stat_get_live_tuples
-------------------------
3
(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)
DROP TABLE trunc_stats_test, trunc_stats_test1, trunc_stats_test2, trunc_stats_test3, trunc_stats_test4;
DROP TABLE prevstats;
-----
-- Test that last_seq_scan, last_idx_scan are correctly maintained
--
-- Perform test using a temporary table. That way autovacuum etc won't
-- interfere. To be able to check that timestamps increase, we sleep for 100ms
-- between tests, assuming that there aren't systems with a coarser timestamp
-- granularity.
-----
BEGIN;
CREATE TEMPORARY TABLE test_last_scan(idx_col int primary key, noidx_col int);
INSERT INTO test_last_scan(idx_col, noidx_col) VALUES(1, 1);
SELECT pg_stat_force_next_flush();
pg_stat_force_next_flush
--------------------------
(1 row)
SELECT last_seq_scan, last_idx_scan FROM pg_stat_all_tables WHERE relid = 'test_last_scan'::regclass;
last_seq_scan | last_idx_scan
---------------+---------------
|
(1 row)
COMMIT;
SELECT pg_stat_reset_single_table_counters('test_last_scan'::regclass);
pg_stat_reset_single_table_counters
-------------------------------------
(1 row)
SELECT seq_scan, idx_scan FROM pg_stat_all_tables WHERE relid = 'test_last_scan'::regclass;
seq_scan | idx_scan
----------+----------
0 | 0
(1 row)
-- ensure we start out with exactly one index and sequential scan
BEGIN;
SET LOCAL enable_seqscan TO on;
SET LOCAL enable_indexscan TO on;
SET LOCAL enable_bitmapscan TO off;
EXPLAIN (COSTS off) SELECT count(*) FROM test_last_scan WHERE noidx_col = 1;
QUERY PLAN
----------------------------------
Aggregate
-> Seq Scan on test_last_scan
Filter: (noidx_col = 1)
(3 rows)
SELECT count(*) FROM test_last_scan WHERE noidx_col = 1;
count
-------
1
(1 row)
SET LOCAL enable_seqscan TO off;
EXPLAIN (COSTS off) SELECT count(*) FROM test_last_scan WHERE idx_col = 1;
QUERY PLAN
--------------------------------------------------------------
Aggregate
-> Index Scan using test_last_scan_pkey on test_last_scan
Index Cond: (idx_col = 1)
(3 rows)
SELECT count(*) FROM test_last_scan WHERE idx_col = 1;
count
-------
1
(1 row)
SELECT pg_stat_force_next_flush();
pg_stat_force_next_flush
--------------------------
(1 row)
COMMIT;
-- fetch timestamps from before the next test
SELECT last_seq_scan AS test_last_seq, last_idx_scan AS test_last_idx
FROM pg_stat_all_tables WHERE relid = 'test_last_scan'::regclass \gset
SELECT pg_sleep(0.1); -- assume a minimum timestamp granularity of 100ms
pg_sleep
----------
(1 row)
-- cause one sequential scan
BEGIN;
SET LOCAL enable_seqscan TO on;
SET LOCAL enable_indexscan TO off;
SET LOCAL enable_bitmapscan TO off;
EXPLAIN (COSTS off) SELECT count(*) FROM test_last_scan WHERE noidx_col = 1;
QUERY PLAN
----------------------------------
Aggregate
-> Seq Scan on test_last_scan
Filter: (noidx_col = 1)
(3 rows)
SELECT count(*) FROM test_last_scan WHERE noidx_col = 1;
count
-------
1
(1 row)
SELECT pg_stat_force_next_flush();
pg_stat_force_next_flush
--------------------------
(1 row)
COMMIT;
-- check that just sequential scan stats were incremented
SELECT seq_scan, :'test_last_seq' < last_seq_scan AS seq_ok, idx_scan, :'test_last_idx' = last_idx_scan AS idx_ok
FROM pg_stat_all_tables WHERE relid = 'test_last_scan'::regclass;
seq_scan | seq_ok | idx_scan | idx_ok
----------+--------+----------+--------
2 | t | 1 | t
(1 row)
-- fetch timestamps from before the next test
SELECT last_seq_scan AS test_last_seq, last_idx_scan AS test_last_idx
FROM pg_stat_all_tables WHERE relid = 'test_last_scan'::regclass \gset
SELECT pg_sleep(0.1);
pg_sleep
----------
(1 row)
-- cause one index scan
BEGIN;
SET LOCAL enable_seqscan TO off;
SET LOCAL enable_indexscan TO on;
SET LOCAL enable_bitmapscan TO off;
EXPLAIN (COSTS off) SELECT count(*) FROM test_last_scan WHERE idx_col = 1;
QUERY PLAN
--------------------------------------------------------------
Aggregate
-> Index Scan using test_last_scan_pkey on test_last_scan
Index Cond: (idx_col = 1)
(3 rows)
SELECT count(*) FROM test_last_scan WHERE idx_col = 1;
count
-------
1
(1 row)
SELECT pg_stat_force_next_flush();
pg_stat_force_next_flush
--------------------------
(1 row)
COMMIT;
-- check that just index scan stats were incremented
SELECT seq_scan, :'test_last_seq' = last_seq_scan AS seq_ok, idx_scan, :'test_last_idx' < last_idx_scan AS idx_ok
FROM pg_stat_all_tables WHERE relid = 'test_last_scan'::regclass;
seq_scan | seq_ok | idx_scan | idx_ok
----------+--------+----------+--------
2 | t | 2 | t
(1 row)
-- fetch timestamps from before the next test
SELECT last_seq_scan AS test_last_seq, last_idx_scan AS test_last_idx
FROM pg_stat_all_tables WHERE relid = 'test_last_scan'::regclass \gset
SELECT pg_sleep(0.1);
pg_sleep
----------
(1 row)
-- cause one bitmap index scan
BEGIN;
SET LOCAL enable_seqscan TO off;
SET LOCAL enable_indexscan TO off;
SET LOCAL enable_bitmapscan TO on;
EXPLAIN (COSTS off) SELECT count(*) FROM test_last_scan WHERE idx_col = 1;
QUERY PLAN
------------------------------------------------------
Aggregate
-> Bitmap Heap Scan on test_last_scan
Recheck Cond: (idx_col = 1)
-> Bitmap Index Scan on test_last_scan_pkey
Index Cond: (idx_col = 1)
(5 rows)
SELECT count(*) FROM test_last_scan WHERE idx_col = 1;
count
-------
1
(1 row)
SELECT pg_stat_force_next_flush();
pg_stat_force_next_flush
--------------------------
(1 row)
COMMIT;
-- check that just index scan stats were incremented
SELECT seq_scan, :'test_last_seq' = last_seq_scan AS seq_ok, idx_scan, :'test_last_idx' < last_idx_scan AS idx_ok
FROM pg_stat_all_tables WHERE relid = 'test_last_scan'::regclass;
seq_scan | seq_ok | idx_scan | idx_ok
----------+--------+----------+--------
2 | t | 3 | t
(1 row)
-----
-- Test that various stats views are being properly populated
-----
-- Test that sessions is incremented when a new session is started in pg_stat_database
SELECT sessions AS db_stat_sessions FROM pg_stat_database WHERE datname = (SELECT current_database()) \gset
\c
SELECT pg_stat_force_next_flush();
pg_stat_force_next_flush
--------------------------
(1 row)
SELECT sessions > :db_stat_sessions FROM pg_stat_database WHERE datname = (SELECT current_database());
?column?
----------
t
(1 row)
-- Test pg_stat_bgwriter checkpointer-related stats, together with pg_stat_wal
SELECT checkpoints_req AS rqst_ckpts_before FROM pg_stat_bgwriter \gset
-- Test pg_stat_wal (and make a temp table so our temp schema exists)
SELECT wal_bytes AS wal_bytes_before FROM pg_stat_wal \gset
CREATE TEMP TABLE test_stats_temp AS SELECT 17;
DROP TABLE test_stats_temp;
-- Checkpoint twice: The checkpointer reports stats after reporting completion
-- of the checkpoint. But after a second checkpoint we'll see at least the
-- results of the first.
CHECKPOINT;
CHECKPOINT;
SELECT checkpoints_req > :rqst_ckpts_before FROM pg_stat_bgwriter;
?column?
----------
t
(1 row)
SELECT wal_bytes > :wal_bytes_before FROM pg_stat_wal;
?column?
----------
t
(1 row)
-- Test pg_stat_get_backend_idset() and some allied functions.
-- In particular, verify that their notion of backend ID matches
-- our temp schema index.
SELECT (current_schemas(true))[1] = ('pg_temp_' || beid::text) AS match
FROM pg_stat_get_backend_idset() beid
WHERE pg_stat_get_backend_pid(beid) = pg_backend_pid();
match
-------
t
(1 row)
-----
-- Test that resetting stats works for reset timestamp
-----
-- Test that reset_slru with a specified SLRU works.
SELECT stats_reset AS slru_commit_ts_reset_ts FROM pg_stat_slru WHERE name = 'CommitTs' \gset
SELECT stats_reset AS slru_notify_reset_ts FROM pg_stat_slru WHERE name = 'Notify' \gset
SELECT pg_stat_reset_slru('CommitTs');
pg_stat_reset_slru
--------------------
(1 row)
SELECT stats_reset > :'slru_commit_ts_reset_ts'::timestamptz FROM pg_stat_slru WHERE name = 'CommitTs';
?column?
----------
t
(1 row)
SELECT stats_reset AS slru_commit_ts_reset_ts FROM pg_stat_slru WHERE name = 'CommitTs' \gset
-- Test that multiple SLRUs are reset when no specific SLRU provided to reset function
SELECT pg_stat_reset_slru(NULL);
pg_stat_reset_slru
--------------------
(1 row)
SELECT stats_reset > :'slru_commit_ts_reset_ts'::timestamptz FROM pg_stat_slru WHERE name = 'CommitTs';
?column?
----------
t
(1 row)
SELECT stats_reset > :'slru_notify_reset_ts'::timestamptz FROM pg_stat_slru WHERE name = 'Notify';
?column?
----------
t
(1 row)
-- Test that reset_shared with archiver specified as the stats type works
SELECT stats_reset AS archiver_reset_ts FROM pg_stat_archiver \gset
SELECT pg_stat_reset_shared('archiver');
pg_stat_reset_shared
----------------------
(1 row)
SELECT stats_reset > :'archiver_reset_ts'::timestamptz FROM pg_stat_archiver;
?column?
----------
t
(1 row)
SELECT stats_reset AS archiver_reset_ts FROM pg_stat_archiver \gset
-- Test that reset_shared with bgwriter specified as the stats type works
SELECT stats_reset AS bgwriter_reset_ts FROM pg_stat_bgwriter \gset
SELECT pg_stat_reset_shared('bgwriter');
pg_stat_reset_shared
----------------------
(1 row)
SELECT stats_reset > :'bgwriter_reset_ts'::timestamptz FROM pg_stat_bgwriter;
?column?
----------
t
(1 row)
SELECT stats_reset AS bgwriter_reset_ts FROM pg_stat_bgwriter \gset
-- Test that reset_shared with wal specified as the stats type works
SELECT stats_reset AS wal_reset_ts FROM pg_stat_wal \gset
SELECT pg_stat_reset_shared('wal');
pg_stat_reset_shared
----------------------
(1 row)
SELECT stats_reset > :'wal_reset_ts'::timestamptz FROM pg_stat_wal;
?column?
----------
t
(1 row)
SELECT stats_reset AS wal_reset_ts FROM pg_stat_wal \gset
-- Test that reset_shared with no specified stats type doesn't reset anything
SELECT pg_stat_reset_shared(NULL);
pg_stat_reset_shared
----------------------
(1 row)
SELECT stats_reset = :'archiver_reset_ts'::timestamptz FROM pg_stat_archiver;
?column?
----------
t
(1 row)
SELECT stats_reset = :'bgwriter_reset_ts'::timestamptz FROM pg_stat_bgwriter;
?column?
----------
t
(1 row)
SELECT stats_reset = :'wal_reset_ts'::timestamptz FROM pg_stat_wal;
?column?
----------
t
(1 row)
-- Test that reset works for pg_stat_database
-- Since pg_stat_database stats_reset starts out as NULL, reset it once first so we have something to compare it to
SELECT pg_stat_reset();
pg_stat_reset
---------------
(1 row)
SELECT stats_reset AS db_reset_ts FROM pg_stat_database WHERE datname = (SELECT current_database()) \gset
SELECT pg_stat_reset();
pg_stat_reset
---------------
(1 row)
SELECT stats_reset > :'db_reset_ts'::timestamptz FROM pg_stat_database WHERE datname = (SELECT current_database());
?column?
----------
t
(1 row)
----
-- pg_stat_get_snapshot_timestamp behavior
----
BEGIN;
SET LOCAL stats_fetch_consistency = snapshot;
-- no snapshot yet, return NULL
SELECT pg_stat_get_snapshot_timestamp();
pg_stat_get_snapshot_timestamp
--------------------------------
(1 row)
-- any attempt at accessing stats will build snapshot
SELECT pg_stat_get_function_calls(0);
pg_stat_get_function_calls
----------------------------
(1 row)
SELECT pg_stat_get_snapshot_timestamp() >= NOW();
?column?
----------
t
(1 row)
-- shows NULL again after clearing
SELECT pg_stat_clear_snapshot();
pg_stat_clear_snapshot
------------------------
(1 row)
SELECT pg_stat_get_snapshot_timestamp();
pg_stat_get_snapshot_timestamp
--------------------------------
(1 row)
COMMIT;
----
-- pg_stat_have_stats behavior
----
-- fixed-numbered stats exist
SELECT pg_stat_have_stats('bgwriter', 0, 0);
pg_stat_have_stats
--------------------
t
(1 row)
-- unknown stats kinds error out
SELECT pg_stat_have_stats('zaphod', 0, 0);
ERROR: invalid statistics kind: "zaphod"
-- db stats have objoid 0
SELECT pg_stat_have_stats('database', :dboid, 1);
pg_stat_have_stats
--------------------
f
(1 row)
SELECT pg_stat_have_stats('database', :dboid, 0);
pg_stat_have_stats
--------------------
t
(1 row)
-- pg_stat_have_stats returns true for committed index creation
CREATE table stats_test_tab1 as select generate_series(1,10) a;
CREATE index stats_test_idx1 on stats_test_tab1(a);
SELECT 'stats_test_idx1'::regclass::oid AS stats_test_idx1_oid \gset
SET enable_seqscan TO off;
select a from stats_test_tab1 where a = 3;
a
---
3
(1 row)
SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid);
pg_stat_have_stats
--------------------
t
(1 row)
-- pg_stat_have_stats returns false for dropped index with stats
SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid);
pg_stat_have_stats
--------------------
t
(1 row)
DROP index stats_test_idx1;
SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid);
pg_stat_have_stats
--------------------
f
(1 row)
-- pg_stat_have_stats returns false for rolled back index creation
BEGIN;
CREATE index stats_test_idx1 on stats_test_tab1(a);
SELECT 'stats_test_idx1'::regclass::oid AS stats_test_idx1_oid \gset
select a from stats_test_tab1 where a = 3;
a
---
3
(1 row)
SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid);
pg_stat_have_stats
--------------------
t
(1 row)
ROLLBACK;
SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid);
pg_stat_have_stats
--------------------
f
(1 row)
-- pg_stat_have_stats returns true for reindex CONCURRENTLY
CREATE index stats_test_idx1 on stats_test_tab1(a);
SELECT 'stats_test_idx1'::regclass::oid AS stats_test_idx1_oid \gset
select a from stats_test_tab1 where a = 3;
a
---
3
(1 row)
SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid);
pg_stat_have_stats
--------------------
t
(1 row)
REINDEX index CONCURRENTLY stats_test_idx1;
-- false for previous oid
SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid);
pg_stat_have_stats
--------------------
f
(1 row)
-- true for new oid
SELECT 'stats_test_idx1'::regclass::oid AS stats_test_idx1_oid \gset
SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid);
pg_stat_have_stats
--------------------
t
(1 row)
-- pg_stat_have_stats returns true for a rolled back drop index with stats
BEGIN;
SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid);
pg_stat_have_stats
--------------------
t
(1 row)
DROP index stats_test_idx1;
ROLLBACK;
SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid);
pg_stat_have_stats
--------------------
t
(1 row)
-- put enable_seqscan back to on
SET enable_seqscan TO on;
-- ensure that stats accessors handle NULL input correctly
SELECT pg_stat_get_replication_slot(NULL);
pg_stat_get_replication_slot
------------------------------
(1 row)
SELECT pg_stat_get_subscription_stats(NULL);
pg_stat_get_subscription_stats
--------------------------------
(1 row)
-- End of Stats Test