1--
2-- Test Statistics Collector
3--
4-- Must be run after tenk2 has been created (by create_table),
5-- populated (by create_misc) and indexed (by create_index).
6--
7-- conditio sine qua non
8SHOW track_counts;  -- must be on
9 track_counts
10--------------
11 on
12(1 row)
13
14-- ensure that both seqscan and indexscan plans are allowed
15SET enable_seqscan TO on;
16SET enable_indexscan TO on;
17-- for the moment, we don't want index-only scans here
18SET enable_indexonlyscan TO off;
19-- wait to let any prior tests finish dumping out stats;
20-- else our messages might get lost due to contention
21SELECT pg_sleep_for('2 seconds');
22 pg_sleep_for
23--------------
24
25(1 row)
26
27-- save counters
28CREATE TEMP TABLE prevstats AS
29SELECT t.seq_scan, t.seq_tup_read, t.idx_scan, t.idx_tup_fetch,
30       (b.heap_blks_read + b.heap_blks_hit) AS heap_blks,
31       (b.idx_blks_read + b.idx_blks_hit) AS idx_blks,
32       pg_stat_get_snapshot_timestamp() as snap_ts
33  FROM pg_catalog.pg_stat_user_tables AS t,
34       pg_catalog.pg_statio_user_tables AS b
35 WHERE t.relname='tenk2' AND b.relname='tenk2';
36-- function to wait for counters to advance
37create function wait_for_stats() returns void as $$
38declare
39  start_time timestamptz := clock_timestamp();
40  updated1 bool;
41  updated2 bool;
42  updated3 bool;
43begin
44  -- we don't want to wait forever; loop will exit after 30 seconds
45  for i in 1 .. 300 loop
46
47    -- With parallel query, the seqscan and indexscan on tenk2 might be done
48    -- in parallel worker processes, which will send their stats counters
49    -- asynchronously to what our own session does.  So we must check for
50    -- those counts to be registered separately from the update counts.
51
52    -- check to see if seqscan has been sensed
53    SELECT (st.seq_scan >= pr.seq_scan + 1) INTO updated1
54      FROM pg_stat_user_tables AS st, pg_class AS cl, prevstats AS pr
55     WHERE st.relname='tenk2' AND cl.relname='tenk2';
56
57    -- check to see if indexscan has been sensed
58    SELECT (st.idx_scan >= pr.idx_scan + 1) INTO updated2
59      FROM pg_stat_user_tables AS st, pg_class AS cl, prevstats AS pr
60     WHERE st.relname='tenk2' AND cl.relname='tenk2';
61
62    -- check to see if all updates have been sensed
63    SELECT (n_tup_ins > 0) INTO updated3
64      FROM pg_stat_user_tables WHERE relname='trunc_stats_test4';
65
66    exit when updated1 and updated2 and updated3;
67
68    -- wait a little
69    perform pg_sleep(0.1);
70
71    -- reset stats snapshot so we can test again
72    perform pg_stat_clear_snapshot();
73
74  end loop;
75
76  -- report time waited in postmaster log (where it won't change test output)
77  raise log 'wait_for_stats delayed % seconds',
78    extract(epoch from clock_timestamp() - start_time);
79end
80$$ language plpgsql;
81-- test effects of TRUNCATE on n_live_tup/n_dead_tup counters
82CREATE TABLE trunc_stats_test(id serial);
83CREATE TABLE trunc_stats_test1(id serial, stuff text);
84CREATE TABLE trunc_stats_test2(id serial);
85CREATE TABLE trunc_stats_test3(id serial, stuff text);
86CREATE TABLE trunc_stats_test4(id serial);
87-- check that n_live_tup is reset to 0 after truncate
88INSERT INTO trunc_stats_test DEFAULT VALUES;
89INSERT INTO trunc_stats_test DEFAULT VALUES;
90INSERT INTO trunc_stats_test DEFAULT VALUES;
91TRUNCATE trunc_stats_test;
92-- test involving a truncate in a transaction; 4 ins but only 1 live
93INSERT INTO trunc_stats_test1 DEFAULT VALUES;
94INSERT INTO trunc_stats_test1 DEFAULT VALUES;
95INSERT INTO trunc_stats_test1 DEFAULT VALUES;
96UPDATE trunc_stats_test1 SET id = id + 10 WHERE id IN (1, 2);
97DELETE FROM trunc_stats_test1 WHERE id = 3;
98BEGIN;
99UPDATE trunc_stats_test1 SET id = id + 100;
100TRUNCATE trunc_stats_test1;
101INSERT INTO trunc_stats_test1 DEFAULT VALUES;
102COMMIT;
103-- use a savepoint: 1 insert, 1 live
104BEGIN;
105INSERT INTO trunc_stats_test2 DEFAULT VALUES;
106INSERT INTO trunc_stats_test2 DEFAULT VALUES;
107SAVEPOINT p1;
108INSERT INTO trunc_stats_test2 DEFAULT VALUES;
109TRUNCATE trunc_stats_test2;
110INSERT INTO trunc_stats_test2 DEFAULT VALUES;
111RELEASE SAVEPOINT p1;
112COMMIT;
113-- rollback a savepoint: this should count 4 inserts and have 2
114-- live tuples after commit (and 2 dead ones due to aborted subxact)
115BEGIN;
116INSERT INTO trunc_stats_test3 DEFAULT VALUES;
117INSERT INTO trunc_stats_test3 DEFAULT VALUES;
118SAVEPOINT p1;
119INSERT INTO trunc_stats_test3 DEFAULT VALUES;
120INSERT INTO trunc_stats_test3 DEFAULT VALUES;
121TRUNCATE trunc_stats_test3;
122INSERT INTO trunc_stats_test3 DEFAULT VALUES;
123ROLLBACK TO SAVEPOINT p1;
124COMMIT;
125-- rollback a truncate: this should count 2 inserts and produce 2 dead tuples
126BEGIN;
127INSERT INTO trunc_stats_test4 DEFAULT VALUES;
128INSERT INTO trunc_stats_test4 DEFAULT VALUES;
129TRUNCATE trunc_stats_test4;
130INSERT INTO trunc_stats_test4 DEFAULT VALUES;
131ROLLBACK;
132-- do a seqscan
133SELECT count(*) FROM tenk2;
134 count
135-------
136 10000
137(1 row)
138
139-- do an indexscan
140SELECT count(*) FROM tenk2 WHERE unique1 = 1;
141 count
142-------
143     1
144(1 row)
145
146-- force the rate-limiting logic in pgstat_report_stat() to time out
147-- and send a message
148SELECT pg_sleep(1.0);
149 pg_sleep
150----------
151
152(1 row)
153
154-- wait for stats collector to update
155SELECT wait_for_stats();
156 wait_for_stats
157----------------
158
159(1 row)
160
161-- check effects
162SELECT relname, n_tup_ins, n_tup_upd, n_tup_del, n_live_tup, n_dead_tup
163  FROM pg_stat_user_tables
164 WHERE relname like 'trunc_stats_test%' order by relname;
165      relname      | n_tup_ins | n_tup_upd | n_tup_del | n_live_tup | n_dead_tup
166-------------------+-----------+-----------+-----------+------------+------------
167 trunc_stats_test  |         3 |         0 |         0 |          0 |          0
168 trunc_stats_test1 |         4 |         2 |         1 |          1 |          0
169 trunc_stats_test2 |         1 |         0 |         0 |          1 |          0
170 trunc_stats_test3 |         4 |         0 |         0 |          2 |          2
171 trunc_stats_test4 |         2 |         0 |         0 |          0 |          2
172(5 rows)
173
174SELECT st.seq_scan >= pr.seq_scan + 1,
175       st.seq_tup_read >= pr.seq_tup_read + cl.reltuples,
176       st.idx_scan >= pr.idx_scan + 1,
177       st.idx_tup_fetch >= pr.idx_tup_fetch + 1
178  FROM pg_stat_user_tables AS st, pg_class AS cl, prevstats AS pr
179 WHERE st.relname='tenk2' AND cl.relname='tenk2';
180 ?column? | ?column? | ?column? | ?column?
181----------+----------+----------+----------
182 t        | t        | t        | t
183(1 row)
184
185SELECT st.heap_blks_read + st.heap_blks_hit >= pr.heap_blks + cl.relpages,
186       st.idx_blks_read + st.idx_blks_hit >= pr.idx_blks + 1
187  FROM pg_statio_user_tables AS st, pg_class AS cl, prevstats AS pr
188 WHERE st.relname='tenk2' AND cl.relname='tenk2';
189 ?column? | ?column?
190----------+----------
191 t        | t
192(1 row)
193
194SELECT pr.snap_ts < pg_stat_get_snapshot_timestamp() as snapshot_newer
195FROM prevstats AS pr;
196 snapshot_newer
197----------------
198 t
199(1 row)
200
201DROP TABLE trunc_stats_test, trunc_stats_test1, trunc_stats_test2, trunc_stats_test3, trunc_stats_test4;
202-- End of Stats Test
203