Relax regression test for fsync check of backend-level stats
[pgsql.git] / src / test / regress / sql / stats.sql
blob399c72bbcf71038cb70f112fd593b7ece5080cf9
1 --
2 -- Test cumulative stats system
3 --
4 -- Must be run after tenk2 has been created (by create_table),
5 -- populated (by create_misc) and indexed (by create_index).
6 --
8 -- conditio sine qua non
9 SHOW track_counts;  -- must be on
11 -- ensure that both seqscan and indexscan plans are allowed
12 SET enable_seqscan TO on;
13 SET enable_indexscan TO on;
14 -- for the moment, we don't want index-only scans here
15 SET enable_indexonlyscan TO off;
16 -- not enabled by default, but we want to test it...
17 SET track_functions TO 'all';
19 -- record dboid for later use
20 SELECT oid AS dboid from pg_database where datname = current_database() \gset
22 -- save counters
23 BEGIN;
24 SET LOCAL stats_fetch_consistency = snapshot;
25 CREATE TABLE prevstats AS
26 SELECT t.seq_scan, t.seq_tup_read, t.idx_scan, t.idx_tup_fetch,
27        (b.heap_blks_read + b.heap_blks_hit) AS heap_blks,
28        (b.idx_blks_read + b.idx_blks_hit) AS idx_blks,
29        pg_stat_get_snapshot_timestamp() as snap_ts
30   FROM pg_catalog.pg_stat_user_tables AS t,
31        pg_catalog.pg_statio_user_tables AS b
32  WHERE t.relname='tenk2' AND b.relname='tenk2';
33 COMMIT;
35 -- test effects of TRUNCATE on n_live_tup/n_dead_tup counters
36 CREATE TABLE trunc_stats_test(id serial);
37 CREATE TABLE trunc_stats_test1(id serial, stuff text);
38 CREATE TABLE trunc_stats_test2(id serial);
39 CREATE TABLE trunc_stats_test3(id serial, stuff text);
40 CREATE TABLE trunc_stats_test4(id serial);
42 -- check that n_live_tup is reset to 0 after truncate
43 INSERT INTO trunc_stats_test DEFAULT VALUES;
44 INSERT INTO trunc_stats_test DEFAULT VALUES;
45 INSERT INTO trunc_stats_test DEFAULT VALUES;
46 TRUNCATE trunc_stats_test;
48 -- test involving a truncate in a transaction; 4 ins but only 1 live
49 INSERT INTO trunc_stats_test1 DEFAULT VALUES;
50 INSERT INTO trunc_stats_test1 DEFAULT VALUES;
51 INSERT INTO trunc_stats_test1 DEFAULT VALUES;
52 UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
53 DELETE FROM trunc_stats_test1 WHERE id = 3;
55 BEGIN;
56 UPDATE trunc_stats_test1 SET id = id + 100;
57 TRUNCATE trunc_stats_test1;
58 INSERT INTO trunc_stats_test1 DEFAULT VALUES;
59 COMMIT;
61 -- use a savepoint: 1 insert, 1 live
62 BEGIN;
63 INSERT INTO trunc_stats_test2 DEFAULT VALUES;
64 INSERT INTO trunc_stats_test2 DEFAULT VALUES;
65 SAVEPOINT p1;
66 INSERT INTO trunc_stats_test2 DEFAULT VALUES;
67 TRUNCATE trunc_stats_test2;
68 INSERT INTO trunc_stats_test2 DEFAULT VALUES;
69 RELEASE SAVEPOINT p1;
70 COMMIT;
72 -- rollback a savepoint: this should count 4 inserts and have 2
73 -- live tuples after commit (and 2 dead ones due to aborted subxact)
74 BEGIN;
75 INSERT INTO trunc_stats_test3 DEFAULT VALUES;
76 INSERT INTO trunc_stats_test3 DEFAULT VALUES;
77 SAVEPOINT p1;
78 INSERT INTO trunc_stats_test3 DEFAULT VALUES;
79 INSERT INTO trunc_stats_test3 DEFAULT VALUES;
80 TRUNCATE trunc_stats_test3;
81 INSERT INTO trunc_stats_test3 DEFAULT VALUES;
82 ROLLBACK TO SAVEPOINT p1;
83 COMMIT;
85 -- rollback a truncate: this should count 2 inserts and produce 2 dead tuples
86 BEGIN;
87 INSERT INTO trunc_stats_test4 DEFAULT VALUES;
88 INSERT INTO trunc_stats_test4 DEFAULT VALUES;
89 TRUNCATE trunc_stats_test4;
90 INSERT INTO trunc_stats_test4 DEFAULT VALUES;
91 ROLLBACK;
93 -- do a seqscan
94 SELECT count(*) FROM tenk2;
95 -- do an indexscan
96 -- make sure it is not a bitmap scan, which might skip fetching heap tuples
97 SET enable_bitmapscan TO off;
98 SELECT count(*) FROM tenk2 WHERE unique1 = 1;
99 RESET enable_bitmapscan;
101 -- ensure pending stats are flushed
102 SELECT pg_stat_force_next_flush();
104 -- check effects
105 BEGIN;
106 SET LOCAL stats_fetch_consistency = snapshot;
108 SELECT relname, n_tup_ins, n_tup_upd, n_tup_del, n_live_tup, n_dead_tup
109   FROM pg_stat_user_tables
110  WHERE relname like 'trunc_stats_test%' order by relname;
112 SELECT st.seq_scan >= pr.seq_scan + 1,
113        st.seq_tup_read >= pr.seq_tup_read + cl.reltuples,
114        st.idx_scan >= pr.idx_scan + 1,
115        st.idx_tup_fetch >= pr.idx_tup_fetch + 1
116   FROM pg_stat_user_tables AS st, pg_class AS cl, prevstats AS pr
117  WHERE st.relname='tenk2' AND cl.relname='tenk2';
119 SELECT st.heap_blks_read + st.heap_blks_hit >= pr.heap_blks + cl.relpages,
120        st.idx_blks_read + st.idx_blks_hit >= pr.idx_blks + 1
121   FROM pg_statio_user_tables AS st, pg_class AS cl, prevstats AS pr
122  WHERE st.relname='tenk2' AND cl.relname='tenk2';
124 SELECT pr.snap_ts < pg_stat_get_snapshot_timestamp() as snapshot_newer
125 FROM prevstats AS pr;
127 COMMIT;
129 ----
130 -- Basic tests for track_functions
132 CREATE FUNCTION stats_test_func1() RETURNS VOID LANGUAGE plpgsql AS $$BEGIN END;$$;
133 SELECT 'stats_test_func1()'::regprocedure::oid AS stats_test_func1_oid \gset
134 CREATE FUNCTION stats_test_func2() RETURNS VOID LANGUAGE plpgsql AS $$BEGIN END;$$;
135 SELECT 'stats_test_func2()'::regprocedure::oid AS stats_test_func2_oid \gset
137 -- test that stats are accumulated
138 BEGIN;
139 SET LOCAL stats_fetch_consistency = none;
140 SELECT pg_stat_get_function_calls(:stats_test_func1_oid);
141 SELECT pg_stat_get_xact_function_calls(:stats_test_func1_oid);
142 SELECT stats_test_func1();
143 SELECT pg_stat_get_xact_function_calls(:stats_test_func1_oid);
144 SELECT stats_test_func1();
145 SELECT pg_stat_get_xact_function_calls(:stats_test_func1_oid);
146 SELECT pg_stat_get_function_calls(:stats_test_func1_oid);
147 COMMIT;
149 -- Verify that function stats are not transactional
151 -- rolled back savepoint in committing transaction
152 BEGIN;
153 SELECT stats_test_func2();
154 SAVEPOINT foo;
155 SELECT stats_test_func2();
156 ROLLBACK TO SAVEPOINT foo;
157 SELECT pg_stat_get_xact_function_calls(:stats_test_func2_oid);
158 SELECT stats_test_func2();
159 COMMIT;
161 -- rolled back transaction
162 BEGIN;
163 SELECT stats_test_func2();
164 ROLLBACK;
166 SELECT pg_stat_force_next_flush();
168 -- check collected stats
169 SELECT funcname, calls FROM pg_stat_user_functions WHERE funcid = :stats_test_func1_oid;
170 SELECT funcname, calls FROM pg_stat_user_functions WHERE funcid = :stats_test_func2_oid;
173 -- check that a rolled back drop function stats leaves stats alive
174 BEGIN;
175 SELECT funcname, calls FROM pg_stat_user_functions WHERE funcid = :stats_test_func1_oid;
176 DROP FUNCTION stats_test_func1();
177 -- shouldn't be visible via view
178 SELECT funcname, calls FROM pg_stat_user_functions WHERE funcid = :stats_test_func1_oid;
179 -- but still via oid access
180 SELECT pg_stat_get_function_calls(:stats_test_func1_oid);
181 ROLLBACK;
182 SELECT funcname, calls FROM pg_stat_user_functions WHERE funcid = :stats_test_func1_oid;
183 SELECT pg_stat_get_function_calls(:stats_test_func1_oid);
186 -- check that function dropped in main transaction leaves no stats behind
187 BEGIN;
188 DROP FUNCTION stats_test_func1();
189 COMMIT;
190 SELECT funcname, calls FROM pg_stat_user_functions WHERE funcid = :stats_test_func1_oid;
191 SELECT pg_stat_get_function_calls(:stats_test_func1_oid);
193 -- check that function dropped in a subtransaction leaves no stats behind
194 BEGIN;
195 SELECT stats_test_func2();
196 SAVEPOINT a;
197 SELECT stats_test_func2();
198 SAVEPOINT b;
199 DROP FUNCTION stats_test_func2();
200 COMMIT;
201 SELECT funcname, calls FROM pg_stat_user_functions WHERE funcid = :stats_test_func2_oid;
202 SELECT pg_stat_get_function_calls(:stats_test_func2_oid);
205 -- Check that stats for relations are dropped. For that we need to access stats
206 -- by oid after the DROP TABLE. Save oids.
207 CREATE TABLE drop_stats_test();
208 INSERT INTO drop_stats_test DEFAULT VALUES;
209 SELECT 'drop_stats_test'::regclass::oid AS drop_stats_test_oid \gset
211 CREATE TABLE drop_stats_test_xact();
212 INSERT INTO drop_stats_test_xact DEFAULT VALUES;
213 SELECT 'drop_stats_test_xact'::regclass::oid AS drop_stats_test_xact_oid \gset
215 CREATE TABLE drop_stats_test_subxact();
216 INSERT INTO drop_stats_test_subxact DEFAULT VALUES;
217 SELECT 'drop_stats_test_subxact'::regclass::oid AS drop_stats_test_subxact_oid \gset
219 SELECT pg_stat_force_next_flush();
221 SELECT pg_stat_get_live_tuples(:drop_stats_test_oid);
222 DROP TABLE drop_stats_test;
223 SELECT pg_stat_get_live_tuples(:drop_stats_test_oid);
224 SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_oid);
226 -- check that rollback protects against having stats dropped and that local
227 -- modifications don't pose a problem
228 SELECT pg_stat_get_live_tuples(:drop_stats_test_xact_oid);
229 SELECT pg_stat_get_tuples_inserted(:drop_stats_test_xact_oid);
230 SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_xact_oid);
231 BEGIN;
232 INSERT INTO drop_stats_test_xact DEFAULT VALUES;
233 SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_xact_oid);
234 DROP TABLE drop_stats_test_xact;
235 SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_xact_oid);
236 ROLLBACK;
237 SELECT pg_stat_force_next_flush();
238 SELECT pg_stat_get_live_tuples(:drop_stats_test_xact_oid);
239 SELECT pg_stat_get_tuples_inserted(:drop_stats_test_xact_oid);
241 -- transactional drop
242 SELECT pg_stat_get_live_tuples(:drop_stats_test_xact_oid);
243 SELECT pg_stat_get_tuples_inserted(:drop_stats_test_xact_oid);
244 BEGIN;
245 INSERT INTO drop_stats_test_xact DEFAULT VALUES;
246 SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_xact_oid);
247 DROP TABLE drop_stats_test_xact;
248 SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_xact_oid);
249 COMMIT;
250 SELECT pg_stat_force_next_flush();
251 SELECT pg_stat_get_live_tuples(:drop_stats_test_xact_oid);
252 SELECT pg_stat_get_tuples_inserted(:drop_stats_test_xact_oid);
254 -- savepoint rollback (2 levels)
255 SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
256 BEGIN;
257 INSERT INTO drop_stats_test_subxact DEFAULT VALUES;
258 SAVEPOINT sp1;
259 INSERT INTO drop_stats_test_subxact DEFAULT VALUES;
260 SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_subxact_oid);
261 SAVEPOINT sp2;
262 DROP TABLE drop_stats_test_subxact;
263 ROLLBACK TO SAVEPOINT sp2;
264 SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_subxact_oid);
265 COMMIT;
266 SELECT pg_stat_force_next_flush();
267 SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
269 -- savepoint rolback (1 level)
270 SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
271 BEGIN;
272 SAVEPOINT sp1;
273 DROP TABLE drop_stats_test_subxact;
274 SAVEPOINT sp2;
275 ROLLBACK TO SAVEPOINT sp1;
276 COMMIT;
277 SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
279 -- and now actually drop
280 SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
281 BEGIN;
282 SAVEPOINT sp1;
283 DROP TABLE drop_stats_test_subxact;
284 SAVEPOINT sp2;
285 RELEASE SAVEPOINT sp1;
286 COMMIT;
287 SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
289 DROP TABLE trunc_stats_test, trunc_stats_test1, trunc_stats_test2, trunc_stats_test3, trunc_stats_test4;
290 DROP TABLE prevstats;
293 -----
294 -- Test that last_seq_scan, last_idx_scan are correctly maintained
296 -- Perform test using a temporary table. That way autovacuum etc won't
297 -- interfere. To be able to check that timestamps increase, we sleep for 100ms
298 -- between tests, assuming that there aren't systems with a coarser timestamp
299 -- granularity.
300 -----
302 BEGIN;
303 CREATE TEMPORARY TABLE test_last_scan(idx_col int primary key, noidx_col int);
304 INSERT INTO test_last_scan(idx_col, noidx_col) VALUES(1, 1);
305 SELECT pg_stat_force_next_flush();
306 SELECT last_seq_scan, last_idx_scan FROM pg_stat_all_tables WHERE relid = 'test_last_scan'::regclass;
307 COMMIT;
309 SELECT pg_stat_reset_single_table_counters('test_last_scan'::regclass);
310 SELECT seq_scan, idx_scan FROM pg_stat_all_tables WHERE relid = 'test_last_scan'::regclass;
312 -- ensure we start out with exactly one index and sequential scan
313 BEGIN;
314 SET LOCAL enable_seqscan TO on;
315 SET LOCAL enable_indexscan TO on;
316 SET LOCAL enable_bitmapscan TO off;
317 EXPLAIN (COSTS off) SELECT count(*) FROM test_last_scan WHERE noidx_col = 1;
318 SELECT count(*) FROM test_last_scan WHERE noidx_col = 1;
319 SET LOCAL enable_seqscan TO off;
320 EXPLAIN (COSTS off) SELECT count(*) FROM test_last_scan WHERE idx_col = 1;
321 SELECT count(*) FROM test_last_scan WHERE idx_col = 1;
322 SELECT pg_stat_force_next_flush();
323 COMMIT;
325 -- fetch timestamps from before the next test
326 SELECT last_seq_scan AS test_last_seq, last_idx_scan AS test_last_idx
327 FROM pg_stat_all_tables WHERE relid = 'test_last_scan'::regclass \gset
328 SELECT pg_sleep(0.1); -- assume a minimum timestamp granularity of 100ms
330 -- cause one sequential scan
331 BEGIN;
332 SET LOCAL enable_seqscan TO on;
333 SET LOCAL enable_indexscan TO off;
334 SET LOCAL enable_bitmapscan TO off;
335 EXPLAIN (COSTS off) SELECT count(*) FROM test_last_scan WHERE noidx_col = 1;
336 SELECT count(*) FROM test_last_scan WHERE noidx_col = 1;
337 SELECT pg_stat_force_next_flush();
338 COMMIT;
339 -- check that just sequential scan stats were incremented
340 SELECT seq_scan, :'test_last_seq' < last_seq_scan AS seq_ok, idx_scan, :'test_last_idx' = last_idx_scan AS idx_ok
341 FROM pg_stat_all_tables WHERE relid = 'test_last_scan'::regclass;
343 -- fetch timestamps from before the next test
344 SELECT last_seq_scan AS test_last_seq, last_idx_scan AS test_last_idx
345 FROM pg_stat_all_tables WHERE relid = 'test_last_scan'::regclass \gset
346 SELECT pg_sleep(0.1);
348 -- cause one index scan
349 BEGIN;
350 SET LOCAL enable_seqscan TO off;
351 SET LOCAL enable_indexscan TO on;
352 SET LOCAL enable_bitmapscan TO off;
353 EXPLAIN (COSTS off) SELECT count(*) FROM test_last_scan WHERE idx_col = 1;
354 SELECT count(*) FROM test_last_scan WHERE idx_col = 1;
355 SELECT pg_stat_force_next_flush();
356 COMMIT;
357 -- check that just index scan stats were incremented
358 SELECT seq_scan, :'test_last_seq' = last_seq_scan AS seq_ok, idx_scan, :'test_last_idx' < last_idx_scan AS idx_ok
359 FROM pg_stat_all_tables WHERE relid = 'test_last_scan'::regclass;
361 -- fetch timestamps from before the next test
362 SELECT last_seq_scan AS test_last_seq, last_idx_scan AS test_last_idx
363 FROM pg_stat_all_tables WHERE relid = 'test_last_scan'::regclass \gset
364 SELECT pg_sleep(0.1);
366 -- cause one bitmap index scan
367 BEGIN;
368 SET LOCAL enable_seqscan TO off;
369 SET LOCAL enable_indexscan TO off;
370 SET LOCAL enable_bitmapscan TO on;
371 EXPLAIN (COSTS off) SELECT count(*) FROM test_last_scan WHERE idx_col = 1;
372 SELECT count(*) FROM test_last_scan WHERE idx_col = 1;
373 SELECT pg_stat_force_next_flush();
374 COMMIT;
375 -- check that just index scan stats were incremented
376 SELECT seq_scan, :'test_last_seq' = last_seq_scan AS seq_ok, idx_scan, :'test_last_idx' < last_idx_scan AS idx_ok
377 FROM pg_stat_all_tables WHERE relid = 'test_last_scan'::regclass;
379 -----
380 -- Test reset of some stats for shared table
381 -----
383 -- This updates the comment of the database currently in use in
384 -- pg_shdescription with a fake value, then sets it back to its
385 -- original value.
386 SELECT shobj_description(d.oid, 'pg_database') as description_before
387   FROM pg_database d WHERE datname = current_database() \gset
389 -- force some stats in pg_shdescription.
390 BEGIN;
391 SELECT current_database() as datname \gset
392 COMMENT ON DATABASE :"datname" IS 'This is a test comment';
393 SELECT pg_stat_force_next_flush();
394 COMMIT;
396 -- check that the stats are reset.
397 SELECT (n_tup_ins + n_tup_upd) > 0 AS has_data FROM pg_stat_all_tables
398   WHERE relid = 'pg_shdescription'::regclass;
399 SELECT pg_stat_reset_single_table_counters('pg_shdescription'::regclass);
400 SELECT (n_tup_ins + n_tup_upd) > 0 AS has_data FROM pg_stat_all_tables
401   WHERE relid = 'pg_shdescription'::regclass;
403 -- set back comment
404 \if :{?description_before}
405   COMMENT ON DATABASE :"datname" IS :'description_before';
406 \else
407   COMMENT ON DATABASE :"datname" IS NULL;
408 \endif
410 -----
411 -- Test that various stats views are being properly populated
412 -----
414 -- Test that sessions is incremented when a new session is started in pg_stat_database
415 SELECT sessions AS db_stat_sessions FROM pg_stat_database WHERE datname = (SELECT current_database()) \gset
417 SELECT pg_stat_force_next_flush();
418 SELECT sessions > :db_stat_sessions FROM pg_stat_database WHERE datname = (SELECT current_database());
420 -- Test pg_stat_checkpointer checkpointer-related stats, together with pg_stat_wal
421 SELECT num_requested AS rqst_ckpts_before FROM pg_stat_checkpointer \gset
423 -- Test pg_stat_wal (and make a temp table so our temp schema exists)
424 SELECT wal_bytes AS wal_bytes_before FROM pg_stat_wal \gset
426 CREATE TEMP TABLE test_stats_temp AS SELECT 17;
427 DROP TABLE test_stats_temp;
429 -- Checkpoint twice: The checkpointer reports stats after reporting completion
430 -- of the checkpoint. But after a second checkpoint we'll see at least the
431 -- results of the first.
432 CHECKPOINT;
433 CHECKPOINT;
435 SELECT num_requested > :rqst_ckpts_before FROM pg_stat_checkpointer;
436 SELECT wal_bytes > :wal_bytes_before FROM pg_stat_wal;
438 -- Test pg_stat_get_backend_idset() and some allied functions.
439 -- In particular, verify that their notion of backend ID matches
440 -- our temp schema index.
441 SELECT (current_schemas(true))[1] = ('pg_temp_' || beid::text) AS match
442 FROM pg_stat_get_backend_idset() beid
443 WHERE pg_stat_get_backend_pid(beid) = pg_backend_pid();
445 -----
446 -- Test that resetting stats works for reset timestamp
447 -----
449 -- Test that reset_slru with a specified SLRU works.
450 SELECT stats_reset AS slru_commit_ts_reset_ts FROM pg_stat_slru WHERE name = 'commit_timestamp' \gset
451 SELECT stats_reset AS slru_notify_reset_ts FROM pg_stat_slru WHERE name = 'notify' \gset
452 SELECT pg_stat_reset_slru('commit_timestamp');
453 SELECT stats_reset > :'slru_commit_ts_reset_ts'::timestamptz FROM pg_stat_slru WHERE name = 'commit_timestamp';
454 SELECT stats_reset AS slru_commit_ts_reset_ts FROM pg_stat_slru WHERE name = 'commit_timestamp' \gset
456 -- Test that multiple SLRUs are reset when no specific SLRU provided to reset function
457 SELECT pg_stat_reset_slru();
458 SELECT stats_reset > :'slru_commit_ts_reset_ts'::timestamptz FROM pg_stat_slru WHERE name = 'commit_timestamp';
459 SELECT stats_reset > :'slru_notify_reset_ts'::timestamptz FROM pg_stat_slru WHERE name = 'notify';
461 -- Test that reset_shared with archiver specified as the stats type works
462 SELECT stats_reset AS archiver_reset_ts FROM pg_stat_archiver \gset
463 SELECT pg_stat_reset_shared('archiver');
464 SELECT stats_reset > :'archiver_reset_ts'::timestamptz FROM pg_stat_archiver;
466 -- Test that reset_shared with bgwriter specified as the stats type works
467 SELECT stats_reset AS bgwriter_reset_ts FROM pg_stat_bgwriter \gset
468 SELECT pg_stat_reset_shared('bgwriter');
469 SELECT stats_reset > :'bgwriter_reset_ts'::timestamptz FROM pg_stat_bgwriter;
471 -- Test that reset_shared with checkpointer specified as the stats type works
472 SELECT stats_reset AS checkpointer_reset_ts FROM pg_stat_checkpointer \gset
473 SELECT pg_stat_reset_shared('checkpointer');
474 SELECT stats_reset > :'checkpointer_reset_ts'::timestamptz FROM pg_stat_checkpointer;
476 -- Test that reset_shared with recovery_prefetch specified as the stats type works
477 SELECT stats_reset AS recovery_prefetch_reset_ts FROM pg_stat_recovery_prefetch \gset
478 SELECT pg_stat_reset_shared('recovery_prefetch');
479 SELECT stats_reset > :'recovery_prefetch_reset_ts'::timestamptz FROM pg_stat_recovery_prefetch;
481 -- Test that reset_shared with slru specified as the stats type works
482 SELECT max(stats_reset) AS slru_reset_ts FROM pg_stat_slru \gset
483 SELECT pg_stat_reset_shared('slru');
484 SELECT max(stats_reset) > :'slru_reset_ts'::timestamptz FROM pg_stat_slru;
486 -- Test that reset_shared with wal specified as the stats type works
487 SELECT stats_reset AS wal_reset_ts FROM pg_stat_wal \gset
488 SELECT pg_stat_reset_shared('wal');
489 SELECT stats_reset > :'wal_reset_ts'::timestamptz FROM pg_stat_wal;
491 -- Test error case for reset_shared with unknown stats type
492 SELECT pg_stat_reset_shared('unknown');
494 -- Test that reset works for pg_stat_database
496 -- Since pg_stat_database stats_reset starts out as NULL, reset it once first so we have something to compare it to
497 SELECT pg_stat_reset();
498 SELECT stats_reset AS db_reset_ts FROM pg_stat_database WHERE datname = (SELECT current_database()) \gset
499 SELECT pg_stat_reset();
500 SELECT stats_reset > :'db_reset_ts'::timestamptz FROM pg_stat_database WHERE datname = (SELECT current_database());
503 ----
504 -- pg_stat_get_snapshot_timestamp behavior
505 ----
506 BEGIN;
507 SET LOCAL stats_fetch_consistency = snapshot;
508 -- no snapshot yet, return NULL
509 SELECT pg_stat_get_snapshot_timestamp();
510 -- any attempt at accessing stats will build snapshot
511 SELECT pg_stat_get_function_calls(0);
512 SELECT pg_stat_get_snapshot_timestamp() >= NOW();
513 -- shows NULL again after clearing
514 SELECT pg_stat_clear_snapshot();
515 SELECT pg_stat_get_snapshot_timestamp();
516 COMMIT;
518 ----
519 -- Changing stats_fetch_consistency in a transaction.
520 ----
521 BEGIN;
522 -- Stats filled under the cache mode
523 SET LOCAL stats_fetch_consistency = cache;
524 SELECT pg_stat_get_function_calls(0);
525 SELECT pg_stat_get_snapshot_timestamp() IS NOT NULL AS snapshot_ok;
526 -- Success in accessing pre-existing snapshot data.
527 SET LOCAL stats_fetch_consistency = snapshot;
528 SELECT pg_stat_get_snapshot_timestamp() IS NOT NULL AS snapshot_ok;
529 SELECT pg_stat_get_function_calls(0);
530 SELECT pg_stat_get_snapshot_timestamp() IS NOT NULL AS snapshot_ok;
531 -- Snapshot cleared.
532 SET LOCAL stats_fetch_consistency = none;
533 SELECT pg_stat_get_snapshot_timestamp() IS NOT NULL AS snapshot_ok;
534 SELECT pg_stat_get_function_calls(0);
535 SELECT pg_stat_get_snapshot_timestamp() IS NOT NULL AS snapshot_ok;
536 ROLLBACK;
538 ----
539 -- pg_stat_have_stats behavior
540 ----
541 -- fixed-numbered stats exist
542 SELECT pg_stat_have_stats('bgwriter', 0, 0);
543 -- unknown stats kinds error out
544 SELECT pg_stat_have_stats('zaphod', 0, 0);
545 -- db stats have objid 0
546 SELECT pg_stat_have_stats('database', :dboid, 1);
547 SELECT pg_stat_have_stats('database', :dboid, 0);
549 -- pg_stat_have_stats returns true for committed index creation
550 CREATE table stats_test_tab1 as select generate_series(1,10) a;
551 CREATE index stats_test_idx1 on stats_test_tab1(a);
552 SELECT 'stats_test_idx1'::regclass::oid AS stats_test_idx1_oid \gset
553 SET enable_seqscan TO off;
554 select a from stats_test_tab1 where a = 3;
555 SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid);
557 -- pg_stat_have_stats returns false for dropped index with stats
558 SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid);
559 DROP index stats_test_idx1;
560 SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid);
562 -- pg_stat_have_stats returns false for rolled back index creation
563 BEGIN;
564 CREATE index stats_test_idx1 on stats_test_tab1(a);
565 SELECT 'stats_test_idx1'::regclass::oid AS stats_test_idx1_oid \gset
566 select a from stats_test_tab1 where a = 3;
567 SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid);
568 ROLLBACK;
569 SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid);
571 -- pg_stat_have_stats returns true for reindex CONCURRENTLY
572 CREATE index stats_test_idx1 on stats_test_tab1(a);
573 SELECT 'stats_test_idx1'::regclass::oid AS stats_test_idx1_oid \gset
574 select a from stats_test_tab1 where a = 3;
575 SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid);
576 REINDEX index CONCURRENTLY stats_test_idx1;
577 -- false for previous oid
578 SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid);
579 -- true for new oid
580 SELECT 'stats_test_idx1'::regclass::oid AS stats_test_idx1_oid \gset
581 SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid);
583 -- pg_stat_have_stats returns true for a rolled back drop index with stats
584 BEGIN;
585 SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid);
586 DROP index stats_test_idx1;
587 ROLLBACK;
588 SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid);
590 -- put enable_seqscan back to on
591 SET enable_seqscan TO on;
593 -- ensure that stats accessors handle NULL input correctly
594 SELECT pg_stat_get_replication_slot(NULL);
595 SELECT pg_stat_get_subscription_stats(NULL);
598 -- Test that the following operations are tracked in pg_stat_io and in
599 -- backend stats:
600 -- - reads of target blocks into shared buffers
601 -- - writes of shared buffers to permanent storage
602 -- - extends of relations using shared buffers
603 -- - fsyncs done to ensure the durability of data dirtying shared buffers
604 -- - shared buffer hits
606 -- There is no test for blocks evicted from shared buffers, because we cannot
607 -- be sure of the state of shared buffers at the point the test is run.
609 -- Create a regular table and insert some data to generate IOCONTEXT_NORMAL
610 -- extends.
611 SELECT pid AS checkpointer_pid FROM pg_stat_activity
612   WHERE backend_type = 'checkpointer' \gset
613 SELECT sum(extends) AS io_sum_shared_before_extends
614   FROM pg_stat_io WHERE context = 'normal' AND object = 'relation' \gset
615 SELECT sum(extends) AS my_io_sum_shared_before_extends
616   FROM pg_stat_get_backend_io(pg_backend_pid())
617   WHERE context = 'normal' AND object = 'relation' \gset
618 SELECT sum(writes) AS writes, sum(fsyncs) AS fsyncs
619   FROM pg_stat_io
620   WHERE object = 'relation' \gset io_sum_shared_before_
621 SELECT sum(writes) AS writes, sum(fsyncs) AS fsyncs
622   FROM pg_stat_get_backend_io(pg_backend_pid())
623   WHERE object = 'relation' \gset my_io_sum_shared_before_
624 CREATE TABLE test_io_shared(a int);
625 INSERT INTO test_io_shared SELECT i FROM generate_series(1,100)i;
626 SELECT pg_stat_force_next_flush();
627 SELECT sum(extends) AS io_sum_shared_after_extends
628   FROM pg_stat_io WHERE context = 'normal' AND object = 'relation' \gset
629 SELECT :io_sum_shared_after_extends > :io_sum_shared_before_extends;
630 SELECT sum(extends) AS my_io_sum_shared_after_extends
631   FROM pg_stat_get_backend_io(pg_backend_pid())
632   WHERE context = 'normal' AND object = 'relation' \gset
633 SELECT :my_io_sum_shared_after_extends > :my_io_sum_shared_before_extends;
635 -- After a checkpoint, there should be some additional IOCONTEXT_NORMAL writes
636 -- and fsyncs in the global stats (usually not for the backend).
637 -- See comment above for rationale for two explicit CHECKPOINTs.
638 CHECKPOINT;
639 CHECKPOINT;
640 SELECT sum(writes) AS writes, sum(fsyncs) AS fsyncs
641   FROM pg_stat_io
642   WHERE object = 'relation' \gset io_sum_shared_after_
643 SELECT :io_sum_shared_after_writes > :io_sum_shared_before_writes;
644 SELECT current_setting('fsync') = 'off'
645   OR :io_sum_shared_after_fsyncs > :io_sum_shared_before_fsyncs;
646 SELECT sum(writes) AS writes, sum(fsyncs) AS fsyncs
647   FROM pg_stat_get_backend_io(pg_backend_pid())
648   WHERE object = 'relation' \gset my_io_sum_shared_after_
649 SELECT :my_io_sum_shared_after_writes >= :my_io_sum_shared_before_writes;
650 SELECT current_setting('fsync') = 'off'
651   OR :my_io_sum_shared_after_fsyncs >= :my_io_sum_shared_before_fsyncs;
653 -- Change the tablespace so that the table is rewritten directly, then SELECT
654 -- from it to cause it to be read back into shared buffers.
655 SELECT sum(reads) AS io_sum_shared_before_reads
656   FROM pg_stat_io WHERE context = 'normal' AND object = 'relation' \gset
657 -- Do this in a transaction to prevent spurious failures due to concurrent accesses to our newly
658 -- rewritten table, e.g. by autovacuum.
659 BEGIN;
660 ALTER TABLE test_io_shared SET TABLESPACE regress_tblspace;
661 -- SELECT from the table so that the data is read into shared buffers and
662 -- context 'normal', object 'relation' reads are counted.
663 SELECT COUNT(*) FROM test_io_shared;
664 COMMIT;
665 SELECT pg_stat_force_next_flush();
666 SELECT sum(reads) AS io_sum_shared_after_reads
667   FROM pg_stat_io WHERE context = 'normal' AND object = 'relation'  \gset
668 SELECT :io_sum_shared_after_reads > :io_sum_shared_before_reads;
670 SELECT sum(hits) AS io_sum_shared_before_hits
671   FROM pg_stat_io WHERE context = 'normal' AND object = 'relation' \gset
672 -- Select from the table again to count hits.
673 -- Ensure we generate hits by forcing a nested loop self-join with no
674 -- materialize node. The outer side's buffer will stay pinned, preventing its
675 -- eviction, while we loop through the inner side and generate hits.
676 BEGIN;
677 SET LOCAL enable_nestloop TO on; SET LOCAL enable_mergejoin TO off;
678 SET LOCAL enable_hashjoin TO off; SET LOCAL enable_material TO off;
679 -- ensure plan stays as we expect it to
680 EXPLAIN (COSTS OFF) SELECT COUNT(*) FROM test_io_shared t1 INNER JOIN test_io_shared t2 USING (a);
681 SELECT COUNT(*) FROM test_io_shared t1 INNER JOIN test_io_shared t2 USING (a);
682 COMMIT;
683 SELECT pg_stat_force_next_flush();
684 SELECT sum(hits) AS io_sum_shared_after_hits
685   FROM pg_stat_io WHERE context = 'normal' AND object = 'relation' \gset
686 SELECT :io_sum_shared_after_hits > :io_sum_shared_before_hits;
688 DROP TABLE test_io_shared;
690 -- Test that the follow IOCONTEXT_LOCAL IOOps are tracked in pg_stat_io:
691 -- - eviction of local buffers in order to reuse them
692 -- - reads of temporary table blocks into local buffers
693 -- - writes of local buffers to permanent storage
694 -- - extends of temporary tables
696 -- Set temp_buffers to its minimum so that we can trigger writes with fewer
697 -- inserted tuples. Do so in a new session in case temporary tables have been
698 -- accessed by previous tests in this session.
700 SET temp_buffers TO 100;
701 CREATE TEMPORARY TABLE test_io_local(a int, b TEXT);
702 SELECT sum(extends) AS extends, sum(evictions) AS evictions, sum(writes) AS writes
703   FROM pg_stat_io
704   WHERE context = 'normal' AND object = 'temp relation' \gset io_sum_local_before_
705 -- Insert tuples into the temporary table, generating extends in the stats.
706 -- Insert enough values that we need to reuse and write out dirty local
707 -- buffers, generating evictions and writes.
708 INSERT INTO test_io_local SELECT generate_series(1, 5000) as id, repeat('a', 200);
709 -- Ensure the table is large enough to exceed our temp_buffers setting.
710 SELECT pg_relation_size('test_io_local') / current_setting('block_size')::int8 > 100;
712 SELECT sum(reads) AS io_sum_local_before_reads
713   FROM pg_stat_io WHERE context = 'normal' AND object = 'temp relation' \gset
714 -- Read in evicted buffers, generating reads.
715 SELECT COUNT(*) FROM test_io_local;
716 SELECT pg_stat_force_next_flush();
717 SELECT sum(evictions) AS evictions,
718        sum(reads) AS reads,
719        sum(writes) AS writes,
720        sum(extends) AS extends
721   FROM pg_stat_io
722   WHERE context = 'normal' AND object = 'temp relation'  \gset io_sum_local_after_
723 SELECT :io_sum_local_after_evictions > :io_sum_local_before_evictions,
724        :io_sum_local_after_reads > :io_sum_local_before_reads,
725        :io_sum_local_after_writes > :io_sum_local_before_writes,
726        :io_sum_local_after_extends > :io_sum_local_before_extends;
728 -- Change the tablespaces so that the temporary table is rewritten to other
729 -- local buffers, exercising a different codepath than standard local buffer
730 -- writes.
731 ALTER TABLE test_io_local SET TABLESPACE regress_tblspace;
732 SELECT pg_stat_force_next_flush();
733 SELECT sum(writes) AS io_sum_local_new_tblspc_writes
734   FROM pg_stat_io WHERE context = 'normal' AND object = 'temp relation'  \gset
735 SELECT :io_sum_local_new_tblspc_writes > :io_sum_local_after_writes;
736 RESET temp_buffers;
738 -- Test that reuse of strategy buffers and reads of blocks into these reused
739 -- buffers while VACUUMing are tracked in pg_stat_io. If there is sufficient
740 -- demand for shared buffers from concurrent queries, some buffers may be
741 -- pinned by other backends before they can be reused. In such cases, the
742 -- backend will evict a buffer from outside the ring and add it to the
743 -- ring. This is considered an eviction and not a reuse.
745 -- Set wal_skip_threshold smaller than the expected size of
746 -- test_io_vac_strategy so that, even if wal_level is minimal, VACUUM FULL will
747 -- fsync the newly rewritten test_io_vac_strategy instead of writing it to WAL.
748 -- Writing it to WAL will result in the newly written relation pages being in
749 -- shared buffers -- preventing us from testing BAS_VACUUM BufferAccessStrategy
750 -- reads.
751 SET wal_skip_threshold = '1 kB';
752 SELECT sum(reuses) AS reuses, sum(reads) AS reads, sum(evictions) AS evictions
753   FROM pg_stat_io WHERE context = 'vacuum' \gset io_sum_vac_strategy_before_
754 CREATE TABLE test_io_vac_strategy(a int, b int) WITH (autovacuum_enabled = 'false');
755 INSERT INTO test_io_vac_strategy SELECT i, i from generate_series(1, 4500)i;
756 -- Ensure that the next VACUUM will need to perform IO by rewriting the table
757 -- first with VACUUM (FULL).
758 VACUUM (FULL) test_io_vac_strategy;
759 -- Use the minimum BUFFER_USAGE_LIMIT to cause reuses or evictions with the
760 -- smallest table possible.
761 VACUUM (PARALLEL 0, BUFFER_USAGE_LIMIT 128) test_io_vac_strategy;
762 SELECT pg_stat_force_next_flush();
763 SELECT sum(reuses) AS reuses, sum(reads) AS reads, sum(evictions) AS evictions
764   FROM pg_stat_io WHERE context = 'vacuum' \gset io_sum_vac_strategy_after_
765 SELECT :io_sum_vac_strategy_after_reads > :io_sum_vac_strategy_before_reads;
766 SELECT (:io_sum_vac_strategy_after_reuses + :io_sum_vac_strategy_after_evictions) >
767   (:io_sum_vac_strategy_before_reuses + :io_sum_vac_strategy_before_evictions);
768 RESET wal_skip_threshold;
770 -- Test that extends done by a CTAS, which uses a BAS_BULKWRITE
771 -- BufferAccessStrategy, are tracked in pg_stat_io.
772 SELECT sum(extends) AS io_sum_bulkwrite_strategy_extends_before
773   FROM pg_stat_io WHERE context = 'bulkwrite' \gset
774 CREATE TABLE test_io_bulkwrite_strategy AS SELECT i FROM generate_series(1,100)i;
775 SELECT pg_stat_force_next_flush();
776 SELECT sum(extends) AS io_sum_bulkwrite_strategy_extends_after
777   FROM pg_stat_io WHERE context = 'bulkwrite' \gset
778 SELECT :io_sum_bulkwrite_strategy_extends_after > :io_sum_bulkwrite_strategy_extends_before;
780 -- Test IO stats reset
781 SELECT pg_stat_have_stats('io', 0, 0);
782 SELECT sum(evictions) + sum(reuses) + sum(extends) + sum(fsyncs) + sum(reads) + sum(writes) + sum(writebacks) + sum(hits) AS io_stats_pre_reset
783   FROM pg_stat_io \gset
784 SELECT sum(evictions) + sum(reuses) + sum(extends) + sum(fsyncs) + sum(reads) + sum(writes) + sum(writebacks) + sum(hits) AS my_io_stats_pre_reset
785   FROM pg_stat_get_backend_io(pg_backend_pid()) \gset
786 SELECT pg_stat_reset_shared('io');
787 SELECT sum(evictions) + sum(reuses) + sum(extends) + sum(fsyncs) + sum(reads) + sum(writes) + sum(writebacks) + sum(hits) AS io_stats_post_reset
788   FROM pg_stat_io \gset
789 SELECT :io_stats_post_reset < :io_stats_pre_reset;
790 SELECT sum(evictions) + sum(reuses) + sum(extends) + sum(fsyncs) + sum(reads) + sum(writes) + sum(writebacks) + sum(hits) AS my_io_stats_post_reset
791   FROM pg_stat_get_backend_io(pg_backend_pid()) \gset
792 -- pg_stat_reset_shared() did not reset backend IO stats
793 SELECT :my_io_stats_pre_reset <= :my_io_stats_post_reset;
794 -- but pg_stat_reset_backend_stats() does
795 SELECT pg_stat_reset_backend_stats(pg_backend_pid());
796 SELECT sum(evictions) + sum(reuses) + sum(extends) + sum(fsyncs) + sum(reads) + sum(writes) + sum(writebacks) + sum(hits) AS my_io_stats_post_backend_reset
797   FROM pg_stat_get_backend_io(pg_backend_pid()) \gset
798 SELECT :my_io_stats_pre_reset > :my_io_stats_post_backend_reset;
800 -- Check invalid input for pg_stat_get_backend_io()
801 SELECT pg_stat_get_backend_io(NULL);
802 SELECT pg_stat_get_backend_io(0);
803 -- Auxiliary processes return no data.
804 SELECT pg_stat_get_backend_io(:checkpointer_pid);
806 -- test BRIN index doesn't block HOT update
807 CREATE TABLE brin_hot (
808   id  integer PRIMARY KEY,
809   val integer NOT NULL
810 ) WITH (autovacuum_enabled = off, fillfactor = 70);
812 INSERT INTO brin_hot SELECT *, 0 FROM generate_series(1, 235);
813 CREATE INDEX val_brin ON brin_hot using brin(val);
815 CREATE FUNCTION wait_for_hot_stats() RETURNS void AS $$
816 DECLARE
817   start_time timestamptz := clock_timestamp();
818   updated bool;
819 BEGIN
820   -- we don't want to wait forever; loop will exit after 30 seconds
821   FOR i IN 1 .. 300 LOOP
822     SELECT (pg_stat_get_tuples_hot_updated('brin_hot'::regclass::oid) > 0) INTO updated;
823     EXIT WHEN updated;
825     -- wait a little
826     PERFORM pg_sleep_for('100 milliseconds');
827     -- reset stats snapshot so we can test again
828     PERFORM pg_stat_clear_snapshot();
829   END LOOP;
830   -- report time waited in postmaster log (where it won't change test output)
831   RAISE log 'wait_for_hot_stats delayed % seconds',
832     EXTRACT(epoch FROM clock_timestamp() - start_time);
834 $$ LANGUAGE plpgsql;
836 UPDATE brin_hot SET val = -3 WHERE id = 42;
838 -- We can't just call wait_for_hot_stats() at this point, because we only
839 -- transmit stats when the session goes idle, and we probably didn't
840 -- transmit the last couple of counts yet thanks to the rate-limiting logic
841 -- in pgstat_report_stat().  But instead of waiting for the rate limiter's
842 -- timeout to elapse, let's just start a new session.  The old one will
843 -- then send its stats before dying.
844 \c -
846 SELECT wait_for_hot_stats();
847 SELECT pg_stat_get_tuples_hot_updated('brin_hot'::regclass::oid);
849 DROP TABLE brin_hot;
850 DROP FUNCTION wait_for_hot_stats();
852 -- Test handling of index predicates - updating attributes in precicates
853 -- should not block HOT when summarizing indexes are involved. We update
854 -- a row that was not indexed due to the index predicate, and becomes
855 -- indexable - the HOT-updated tuple is forwarded to the BRIN index.
856 CREATE TABLE brin_hot_2 (a int, b int);
857 INSERT INTO brin_hot_2 VALUES (1, 100);
858 CREATE INDEX ON brin_hot_2 USING brin (b) WHERE a = 2;
860 UPDATE brin_hot_2 SET a = 2;
862 EXPLAIN (COSTS OFF) SELECT * FROM brin_hot_2 WHERE a = 2 AND b = 100;
863 SELECT COUNT(*) FROM brin_hot_2 WHERE a = 2 AND b = 100;
865 SET enable_seqscan = off;
867 EXPLAIN (COSTS OFF) SELECT * FROM brin_hot_2 WHERE a = 2 AND b = 100;
868 SELECT COUNT(*) FROM brin_hot_2 WHERE a = 2 AND b = 100;
870 DROP TABLE brin_hot_2;
872 -- Test that updates to indexed columns are still propagated to the
873 -- BRIN column.
874 -- https://postgr.es/m/05ebcb44-f383-86e3-4f31-0a97a55634cf@enterprisedb.com
875 CREATE TABLE brin_hot_3 (a int, filler text) WITH (fillfactor = 10);
876 INSERT INTO brin_hot_3 SELECT 1, repeat(' ', 500) FROM generate_series(1, 20);
877 CREATE INDEX ON brin_hot_3 USING brin (a) WITH (pages_per_range = 1);
878 UPDATE brin_hot_3 SET a = 2;
880 EXPLAIN (COSTS OFF) SELECT * FROM brin_hot_3 WHERE a = 2;
881 SELECT COUNT(*) FROM brin_hot_3 WHERE a = 2;
883 DROP TABLE brin_hot_3;
885 SET enable_seqscan = on;
887 -- End of Stats Test