diff --git a/contrib/pg_stat_statements/Makefile b/contrib/pg_stat_statements/Makefile index edc40c8bbfb..f235f73df56 100644 --- a/contrib/pg_stat_statements/Makefile +++ b/contrib/pg_stat_statements/Makefile @@ -17,7 +17,7 @@ PGFILEDESC = "pg_stat_statements - execution statistics of SQL statements" LDFLAGS_SL += $(filter -lm, $(LIBS)) REGRESS_OPTS = --temp-config $(top_srcdir)/contrib/pg_stat_statements/pg_stat_statements.conf -REGRESS = pg_stat_statements oldextversions +REGRESS = pg_stat_statements utility level_tracking planning cleanup oldextversions # Disabled because these tests require "shared_preload_libraries=pg_stat_statements", # which typical installcheck users do not have (e.g. buildfarm clients). NO_INSTALLCHECK = 1 diff --git a/contrib/pg_stat_statements/expected/cleanup.out b/contrib/pg_stat_statements/expected/cleanup.out new file mode 100644 index 00000000000..36bec35c406 --- /dev/null +++ b/contrib/pg_stat_statements/expected/cleanup.out @@ -0,0 +1 @@ +DROP EXTENSION pg_stat_statements; diff --git a/contrib/pg_stat_statements/expected/level_tracking.out b/contrib/pg_stat_statements/expected/level_tracking.out new file mode 100644 index 00000000000..c824ebdac5d --- /dev/null +++ b/contrib/pg_stat_statements/expected/level_tracking.out @@ -0,0 +1,204 @@ +-- +-- Statement level tracking +-- +SET pg_stat_statements.track_utility = TRUE; +SELECT pg_stat_statements_reset(); + pg_stat_statements_reset +-------------------------- + +(1 row) + +-- DO block - top-level tracking. +CREATE TABLE stats_track_tab (x int); +SET pg_stat_statements.track = 'top'; +DELETE FROM stats_track_tab; +DO $$ +BEGIN + DELETE FROM stats_track_tab; +END; +$$ LANGUAGE plpgsql; +SELECT toplevel, calls, query FROM pg_stat_statements + WHERE query LIKE '%DELETE%' ORDER BY query COLLATE "C", toplevel; + toplevel | calls | query +----------+-------+-------------------------------- + t | 1 | DELETE FROM stats_track_tab + t | 1 | DO $$ + + | | BEGIN + + | | DELETE FROM stats_track_tab;+ + | | END; + + | | $$ LANGUAGE plpgsql +(2 rows) + +SELECT pg_stat_statements_reset(); + pg_stat_statements_reset +-------------------------- + +(1 row) + +-- DO block - all-level tracking. +SET pg_stat_statements.track = 'all'; +DELETE FROM stats_track_tab; +DO $$ +BEGIN + DELETE FROM stats_track_tab; +END; $$; +DO LANGUAGE plpgsql $$ +BEGIN + -- this is a SELECT + PERFORM 'hello world'::TEXT; +END; $$; +SELECT toplevel, calls, query FROM pg_stat_statements + ORDER BY query COLLATE "C", toplevel; + toplevel | calls | query +----------+-------+-------------------------------------- + f | 1 | DELETE FROM stats_track_tab + t | 1 | DELETE FROM stats_track_tab + t | 1 | DO $$ + + | | BEGIN + + | | DELETE FROM stats_track_tab; + + | | END; $$ + t | 1 | DO LANGUAGE plpgsql $$ + + | | BEGIN + + | | -- this is a SELECT + + | | PERFORM 'hello world'::TEXT; + + | | END; $$ + f | 1 | SELECT $1::TEXT + t | 1 | SELECT pg_stat_statements_reset() + t | 1 | SET pg_stat_statements.track = 'all' +(7 rows) + +-- PL/pgSQL function - top-level tracking. +SET pg_stat_statements.track = 'top'; +SET pg_stat_statements.track_utility = FALSE; +SELECT pg_stat_statements_reset(); + pg_stat_statements_reset +-------------------------- + +(1 row) + +CREATE FUNCTION PLUS_TWO(i INTEGER) RETURNS INTEGER AS $$ +DECLARE + r INTEGER; +BEGIN + SELECT (i + 1 + 1.0)::INTEGER INTO r; + RETURN r; +END; $$ LANGUAGE plpgsql; +SELECT PLUS_TWO(3); + plus_two +---------- + 5 +(1 row) + +SELECT PLUS_TWO(7); + plus_two +---------- + 9 +(1 row) + +-- SQL function --- use LIMIT to keep it from being inlined +CREATE FUNCTION PLUS_ONE(i INTEGER) RETURNS INTEGER AS +$$ SELECT (i + 1.0)::INTEGER LIMIT 1 $$ LANGUAGE SQL; +SELECT PLUS_ONE(8); + plus_one +---------- + 9 +(1 row) + +SELECT PLUS_ONE(10); + plus_one +---------- + 11 +(1 row) + +SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C"; + calls | rows | query +-------+------+----------------------------------- + 2 | 2 | SELECT PLUS_ONE($1) + 2 | 2 | SELECT PLUS_TWO($1) + 1 | 1 | SELECT pg_stat_statements_reset() +(3 rows) + +-- PL/pgSQL function - all-level tracking. +SET pg_stat_statements.track = 'all'; +SELECT pg_stat_statements_reset(); + pg_stat_statements_reset +-------------------------- + +(1 row) + +-- we drop and recreate the functions to avoid any caching funnies +DROP FUNCTION PLUS_ONE(INTEGER); +DROP FUNCTION PLUS_TWO(INTEGER); +-- PL/pgSQL function +CREATE FUNCTION PLUS_TWO(i INTEGER) RETURNS INTEGER AS $$ +DECLARE + r INTEGER; +BEGIN + SELECT (i + 1 + 1.0)::INTEGER INTO r; + RETURN r; +END; $$ LANGUAGE plpgsql; +SELECT PLUS_TWO(-1); + plus_two +---------- + 1 +(1 row) + +SELECT PLUS_TWO(2); + plus_two +---------- + 4 +(1 row) + +-- SQL function --- use LIMIT to keep it from being inlined +CREATE FUNCTION PLUS_ONE(i INTEGER) RETURNS INTEGER AS +$$ SELECT (i + 1.0)::INTEGER LIMIT 1 $$ LANGUAGE SQL; +SELECT PLUS_ONE(3); + plus_one +---------- + 4 +(1 row) + +SELECT PLUS_ONE(1); + plus_one +---------- + 2 +(1 row) + +SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C"; + calls | rows | query +-------+------+----------------------------------- + 2 | 2 | SELECT (i + $2 + $3)::INTEGER + 2 | 2 | SELECT (i + $2)::INTEGER LIMIT $3 + 2 | 2 | SELECT PLUS_ONE($1) + 2 | 2 | SELECT PLUS_TWO($1) + 1 | 1 | SELECT pg_stat_statements_reset() +(5 rows) + +DROP FUNCTION PLUS_ONE(INTEGER); +-- +-- pg_stat_statements.track = none +-- +SET pg_stat_statements.track = 'none'; +SELECT pg_stat_statements_reset(); + pg_stat_statements_reset +-------------------------- + +(1 row) + +SELECT 1 AS "one"; + one +----- + 1 +(1 row) + +SELECT 1 + 1 AS "two"; + two +----- + 2 +(1 row) + +SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C"; + calls | rows | query +-------+------+------- +(0 rows) + diff --git a/contrib/pg_stat_statements/expected/pg_stat_statements.out b/contrib/pg_stat_statements/expected/pg_stat_statements.out index 8c0b2235e81..2c5bed841af 100644 --- a/contrib/pg_stat_statements/expected/pg_stat_statements.out +++ b/contrib/pg_stat_statements/expected/pg_stat_statements.out @@ -291,9 +291,7 @@ INSERT INTO pgss_test VALUES(generate_series(1, 10), 'aaa'); UPDATE pgss_test SET b = 'bbb' WHERE a > 7; DELETE FROM pgss_test WHERE a > 9; -- DROP test table -SET pg_stat_statements.track_utility = TRUE; DROP TABLE pgss_test; -SET pg_stat_statements.track_utility = FALSE; -- Check WAL is generated for the above statements SELECT query, calls, rows, wal_bytes > 0 as wal_bytes_generated, @@ -303,7 +301,6 @@ FROM pg_stat_statements ORDER BY query COLLATE "C"; query | calls | rows | wal_bytes_generated | wal_records_generated | wal_records_ge_rows -----------------------------------------------------------+-------+------+---------------------+-----------------------+--------------------- DELETE FROM pgss_test WHERE a > $1 | 1 | 1 | t | t | t - DROP TABLE pgss_test | 1 | 0 | t | t | t INSERT INTO pgss_test VALUES(generate_series($1, $2), $3) | 1 | 10 | t | t | t SELECT pg_stat_statements_reset() | 1 | 1 | f | f | f SELECT query, calls, rows, +| 0 | 0 | f | f | t @@ -311,157 +308,9 @@ FROM pg_stat_statements ORDER BY query COLLATE "C"; wal_records > $2 as wal_records_generated, +| | | | | wal_records >= rows as wal_records_ge_rows +| | | | | FROM pg_stat_statements ORDER BY query COLLATE "C" | | | | | - SET pg_stat_statements.track_utility = FALSE | 1 | 0 | f | f | t UPDATE pgss_test SET b = $1 WHERE a > $2 | 1 | 3 | t | t | t -(7 rows) - --- --- pg_stat_statements.track = none --- -SET pg_stat_statements.track = 'none'; -SELECT pg_stat_statements_reset(); - pg_stat_statements_reset --------------------------- - -(1 row) - -SELECT 1 AS "one"; - one ------ - 1 -(1 row) - -SELECT 1 + 1 AS "two"; - two ------ - 2 -(1 row) - -SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C"; - query | calls | rows --------+-------+------ -(0 rows) - --- --- pg_stat_statements.track = top --- -SET pg_stat_statements.track = 'top'; -SELECT pg_stat_statements_reset(); - pg_stat_statements_reset --------------------------- - -(1 row) - -DO LANGUAGE plpgsql $$ -BEGIN - -- this is a SELECT - PERFORM 'hello world'::TEXT; -END; -$$; --- PL/pgSQL function -CREATE FUNCTION PLUS_TWO(i INTEGER) RETURNS INTEGER AS $$ -DECLARE - r INTEGER; -BEGIN - SELECT (i + 1 + 1.0)::INTEGER INTO r; - RETURN r; -END; $$ LANGUAGE plpgsql; -SELECT PLUS_TWO(3); - plus_two ----------- - 5 -(1 row) - -SELECT PLUS_TWO(7); - plus_two ----------- - 9 -(1 row) - --- SQL function --- use LIMIT to keep it from being inlined -CREATE FUNCTION PLUS_ONE(i INTEGER) RETURNS INTEGER AS -$$ SELECT (i + 1.0)::INTEGER LIMIT 1 $$ LANGUAGE SQL; -SELECT PLUS_ONE(8); - plus_one ----------- - 9 -(1 row) - -SELECT PLUS_ONE(10); - plus_one ----------- - 11 -(1 row) - -SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C"; - query | calls | rows -------------------------------------------------------------------------------+-------+------ - SELECT $1::TEXT | 1 | 1 - SELECT PLUS_ONE($1) | 2 | 2 - SELECT PLUS_TWO($1) | 2 | 2 - SELECT pg_stat_statements_reset() | 1 | 1 - SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0 (5 rows) --- --- pg_stat_statements.track = all --- -SET pg_stat_statements.track = 'all'; -SELECT pg_stat_statements_reset(); - pg_stat_statements_reset --------------------------- - -(1 row) - --- we drop and recreate the functions to avoid any caching funnies -DROP FUNCTION PLUS_ONE(INTEGER); -DROP FUNCTION PLUS_TWO(INTEGER); --- PL/pgSQL function -CREATE FUNCTION PLUS_TWO(i INTEGER) RETURNS INTEGER AS $$ -DECLARE - r INTEGER; -BEGIN - SELECT (i + 1 + 1.0)::INTEGER INTO r; - RETURN r; -END; $$ LANGUAGE plpgsql; -SELECT PLUS_TWO(-1); - plus_two ----------- - 1 -(1 row) - -SELECT PLUS_TWO(2); - plus_two ----------- - 4 -(1 row) - --- SQL function --- use LIMIT to keep it from being inlined -CREATE FUNCTION PLUS_ONE(i INTEGER) RETURNS INTEGER AS -$$ SELECT (i + 1.0)::INTEGER LIMIT 1 $$ LANGUAGE SQL; -SELECT PLUS_ONE(3); - plus_one ----------- - 4 -(1 row) - -SELECT PLUS_ONE(1); - plus_one ----------- - 2 -(1 row) - -SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C"; - query | calls | rows -------------------------------------------------------------------------------+-------+------ - SELECT (i + $2 + $3)::INTEGER | 2 | 2 - SELECT (i + $2)::INTEGER LIMIT $3 | 2 | 2 - SELECT PLUS_ONE($1) | 2 | 2 - SELECT PLUS_TWO($1) | 2 | 2 - SELECT pg_stat_statements_reset() | 1 | 1 - SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0 -(6 rows) - -- -- queries with locking clauses -- @@ -550,131 +399,10 @@ SELECT calls, query FROM pg_stat_statements ORDER BY query COLLATE "C"; (12 rows) DROP TABLE pgss_a, pgss_b CASCADE; --- --- utility commands --- -SET pg_stat_statements.track_utility = TRUE; -SELECT pg_stat_statements_reset(); - pg_stat_statements_reset --------------------------- - -(1 row) - -SELECT 1; - ?column? ----------- - 1 -(1 row) - -CREATE INDEX test_b ON test(b); -DROP TABLE test \; -DROP TABLE IF EXISTS test \; -DROP FUNCTION PLUS_ONE(INTEGER); -NOTICE: table "test" does not exist, skipping --- This DROP query uses two different strings, still they count as one entry. -DROP TABLE IF EXISTS test \; -Drop Table If Exists test \; -DROP FUNCTION IF EXISTS PLUS_ONE(INTEGER); -NOTICE: table "test" does not exist, skipping -NOTICE: table "test" does not exist, skipping -NOTICE: function plus_one(pg_catalog.int4) does not exist, skipping -DROP FUNCTION PLUS_TWO(INTEGER); --- This SET query uses two different strings, still they count as one entry. -SET work_mem = '1MB'; -Set work_mem = '1MB'; -SET work_mem = '2MB'; -RESET work_mem; -SET enable_seqscan = off; -SET enable_seqscan = on; -RESET enable_seqscan; -SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C"; - query | calls | rows -------------------------------------------------------------------------------+-------+------ - CREATE INDEX test_b ON test(b) | 1 | 0 - DROP FUNCTION IF EXISTS PLUS_ONE(INTEGER) | 1 | 0 - DROP FUNCTION PLUS_ONE(INTEGER) | 1 | 0 - DROP FUNCTION PLUS_TWO(INTEGER) | 1 | 0 - DROP TABLE IF EXISTS test | 3 | 0 - DROP TABLE test | 1 | 0 - RESET enable_seqscan | 1 | 0 - RESET work_mem | 1 | 0 - SELECT $1 | 1 | 1 - SELECT pg_stat_statements_reset() | 1 | 1 - SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0 - SET enable_seqscan = off | 1 | 0 - SET enable_seqscan = on | 1 | 0 - SET work_mem = '1MB' | 2 | 0 - SET work_mem = '2MB' | 1 | 0 -(15 rows) - --- --- Track the total number of rows retrieved or affected by the utility --- commands of COPY, FETCH, CREATE TABLE AS, CREATE MATERIALIZED VIEW, --- REFRESH MATERIALIZED VIEW and SELECT INTO --- -SELECT pg_stat_statements_reset(); - pg_stat_statements_reset --------------------------- - -(1 row) - -CREATE TABLE pgss_ctas AS SELECT a, 'ctas' b FROM generate_series(1, 10) a; -SELECT generate_series(1, 10) c INTO pgss_select_into; -COPY pgss_ctas (a, b) FROM STDIN; -CREATE MATERIALIZED VIEW pgss_matv AS SELECT * FROM pgss_ctas; -REFRESH MATERIALIZED VIEW pgss_matv; -BEGIN; -DECLARE pgss_cursor CURSOR FOR SELECT * FROM pgss_matv; -FETCH NEXT pgss_cursor; - a | b ----+------ - 1 | ctas -(1 row) - -FETCH FORWARD 5 pgss_cursor; - a | b ----+------ - 2 | ctas - 3 | ctas - 4 | ctas - 5 | ctas - 6 | ctas -(5 rows) - -FETCH FORWARD ALL pgss_cursor; - a | b -----+------ - 7 | ctas - 8 | ctas - 9 | ctas - 10 | ctas - 11 | copy - 12 | copy - 13 | copy -(7 rows) - -COMMIT; -SELECT query, plans, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C"; - query | plans | calls | rows --------------------------------------------------------------------------------------+-------+-------+------ - BEGIN | 0 | 1 | 0 - COMMIT | 0 | 1 | 0 - COPY pgss_ctas (a, b) FROM STDIN | 0 | 1 | 3 - CREATE MATERIALIZED VIEW pgss_matv AS SELECT * FROM pgss_ctas | 0 | 1 | 13 - CREATE TABLE pgss_ctas AS SELECT a, 'ctas' b FROM generate_series(1, 10) a | 0 | 1 | 10 - DECLARE pgss_cursor CURSOR FOR SELECT * FROM pgss_matv | 0 | 1 | 0 - FETCH FORWARD 5 pgss_cursor | 0 | 1 | 5 - FETCH FORWARD ALL pgss_cursor | 0 | 1 | 7 - FETCH NEXT pgss_cursor | 0 | 1 | 1 - REFRESH MATERIALIZED VIEW pgss_matv | 0 | 1 | 13 - SELECT generate_series(1, 10) c INTO pgss_select_into | 0 | 1 | 10 - SELECT pg_stat_statements_reset() | 0 | 1 | 1 - SELECT query, plans, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 1 | 0 | 0 -(13 rows) - -- -- Track user activity and reset them -- +SET pg_stat_statements.track_utility = TRUE; SELECT pg_stat_statements_reset(); pg_stat_statements_reset -------------------------- @@ -872,84 +600,6 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C"; -- DROP ROLE regress_stats_user1; DROP ROLE regress_stats_user2; -DROP MATERIALIZED VIEW pgss_matv; -DROP TABLE pgss_ctas; -DROP TABLE pgss_select_into; --- --- [re]plan counting --- -SELECT pg_stat_statements_reset(); - pg_stat_statements_reset --------------------------- - -(1 row) - -CREATE TABLE test (); -PREPARE prep1 AS SELECT COUNT(*) FROM test; -EXECUTE prep1; - count -------- - 0 -(1 row) - -EXECUTE prep1; - count -------- - 0 -(1 row) - -EXECUTE prep1; - count -------- - 0 -(1 row) - -ALTER TABLE test ADD COLUMN x int; -EXECUTE prep1; - count -------- - 0 -(1 row) - -SELECT 42; - ?column? ----------- - 42 -(1 row) - -SELECT 42; - ?column? ----------- - 42 -(1 row) - -SELECT 42; - ?column? ----------- - 42 -(1 row) - -SELECT query, plans, calls, rows FROM pg_stat_statements - WHERE query NOT LIKE 'PREPARE%' ORDER BY query COLLATE "C"; - query | plans | calls | rows -----------------------------------------------------------+-------+-------+------ - ALTER TABLE test ADD COLUMN x int | 0 | 1 | 0 - CREATE TABLE test () | 0 | 1 | 0 - SELECT $1 | 3 | 3 | 3 - SELECT pg_stat_statements_reset() | 0 | 1 | 1 - SELECT query, plans, calls, rows FROM pg_stat_statements+| 1 | 0 | 0 - WHERE query NOT LIKE $1 ORDER BY query COLLATE "C" | | | -(5 rows) - --- for the prepared statement we expect at least one replan, but cache --- invalidations could force more -SELECT query, plans >= 2 AND plans <= calls AS plans_ok, calls, rows FROM pg_stat_statements - WHERE query LIKE 'PREPARE%' ORDER BY query COLLATE "C"; - query | plans_ok | calls | rows ---------------------------------------------+----------+-------+------ - PREPARE prep1 AS SELECT COUNT(*) FROM test | t | 4 | 4 -(1 row) - -- -- access to pg_stat_statements_info view -- @@ -965,46 +615,6 @@ SELECT dealloc FROM pg_stat_statements_info; 0 (1 row) --- --- top level handling --- -SET pg_stat_statements.track = 'top'; -DELETE FROM test; -DO $$ -BEGIN - DELETE FROM test; -END; -$$ LANGUAGE plpgsql; -SELECT query, toplevel, plans, calls FROM pg_stat_statements WHERE query LIKE '%DELETE%' ORDER BY query COLLATE "C", toplevel; - query | toplevel | plans | calls ------------------------+----------+-------+------- - DELETE FROM test | t | 1 | 1 - DO $$ +| t | 0 | 1 - BEGIN +| | | - DELETE FROM test;+| | | - END; +| | | - $$ LANGUAGE plpgsql | | | -(2 rows) - -SET pg_stat_statements.track = 'all'; -DELETE FROM test; -DO $$ -BEGIN - DELETE FROM test; -END; -$$ LANGUAGE plpgsql; -SELECT query, toplevel, plans, calls FROM pg_stat_statements WHERE query LIKE '%DELETE%' ORDER BY query COLLATE "C", toplevel; - query | toplevel | plans | calls ------------------------+----------+-------+------- - DELETE FROM test | f | 1 | 1 - DELETE FROM test | t | 2 | 2 - DO $$ +| t | 0 | 2 - BEGIN +| | | - DELETE FROM test;+| | | - END; +| | | - $$ LANGUAGE plpgsql | | | -(3 rows) - -- FROM [ONLY] CREATE TABLE tbl_inh(id integer); CREATE TABLE tbl_inh_1() INHERITS (tbl_inh); @@ -1156,4 +766,3 @@ SELECT COUNT(*) FROM pg_stat_statements WHERE query LIKE '%SELECT GROUPING%'; 2 (1 row) -DROP EXTENSION pg_stat_statements; diff --git a/contrib/pg_stat_statements/expected/planning.out b/contrib/pg_stat_statements/expected/planning.out new file mode 100644 index 00000000000..c3561dd7da3 --- /dev/null +++ b/contrib/pg_stat_statements/expected/planning.out @@ -0,0 +1,88 @@ +-- +-- Information related to planning +-- +-- These tests require track_planning to be enabled. +SET pg_stat_statements.track_planning = TRUE; +SELECT pg_stat_statements_reset(); + pg_stat_statements_reset +-------------------------- + +(1 row) + +-- +-- [re]plan counting +-- +CREATE TABLE stats_plan_test (); +PREPARE prep1 AS SELECT COUNT(*) FROM stats_plan_test; +EXECUTE prep1; + count +------- + 0 +(1 row) + +EXECUTE prep1; + count +------- + 0 +(1 row) + +EXECUTE prep1; + count +------- + 0 +(1 row) + +ALTER TABLE stats_plan_test ADD COLUMN x int; +EXECUTE prep1; + count +------- + 0 +(1 row) + +SELECT 42; + ?column? +---------- + 42 +(1 row) + +SELECT 42; + ?column? +---------- + 42 +(1 row) + +SELECT 42; + ?column? +---------- + 42 +(1 row) + +SELECT plans, calls, rows, query FROM pg_stat_statements + WHERE query NOT LIKE 'PREPARE%' ORDER BY query COLLATE "C"; + plans | calls | rows | query +-------+-------+------+---------------------------------------------------------- + 0 | 1 | 0 | ALTER TABLE stats_plan_test ADD COLUMN x int + 0 | 1 | 0 | CREATE TABLE stats_plan_test () + 3 | 3 | 3 | SELECT $1 + 0 | 1 | 1 | SELECT pg_stat_statements_reset() + 1 | 0 | 0 | SELECT plans, calls, rows, query FROM pg_stat_statements+ + | | | WHERE query NOT LIKE $1 ORDER BY query COLLATE "C" +(5 rows) + +-- for the prepared statement we expect at least one replan, but cache +-- invalidations could force more +SELECT plans >= 2 AND plans <= calls AS plans_ok, calls, rows, query FROM pg_stat_statements + WHERE query LIKE 'PREPARE%' ORDER BY query COLLATE "C"; + plans_ok | calls | rows | query +----------+-------+------+------------------------------------------------------- + t | 4 | 4 | PREPARE prep1 AS SELECT COUNT(*) FROM stats_plan_test +(1 row) + +-- Cleanup +DROP TABLE stats_plan_test; +SELECT pg_stat_statements_reset(); + pg_stat_statements_reset +-------------------------- + +(1 row) + diff --git a/contrib/pg_stat_statements/expected/utility.out b/contrib/pg_stat_statements/expected/utility.out new file mode 100644 index 00000000000..2395ccd6b45 --- /dev/null +++ b/contrib/pg_stat_statements/expected/utility.out @@ -0,0 +1,132 @@ +-- +-- Utility commands +-- +-- These tests require track_utility to be enabled. +SET pg_stat_statements.track_utility = TRUE; +SELECT pg_stat_statements_reset(); + pg_stat_statements_reset +-------------------------- + +(1 row) + +SELECT 1; + ?column? +---------- + 1 +(1 row) + +CREATE TEMP TABLE stats_util_tab (a int, b char(20)); +CREATE INDEX test_b ON stats_util_tab(b); +DROP TABLE stats_util_tab \; +DROP TABLE IF EXISTS stats_util_tab; +NOTICE: table "stats_util_tab" does not exist, skipping +-- This DROP query uses two different strings, still they count as one entry. +DROP TABLE IF EXISTS stats_util_tab \; +Drop Table If Exists stats_util_tab; +NOTICE: table "stats_util_tab" does not exist, skipping +NOTICE: table "stats_util_tab" does not exist, skipping +SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C"; + calls | rows | query +-------+------+------------------------------------------------------ + 1 | 0 | CREATE INDEX test_b ON stats_util_tab(b) + 1 | 0 | CREATE TEMP TABLE stats_util_tab (a int, b char(20)) + 3 | 0 | DROP TABLE IF EXISTS stats_util_tab + 1 | 0 | DROP TABLE stats_util_tab + 1 | 1 | SELECT $1 + 1 | 1 | SELECT pg_stat_statements_reset() +(6 rows) + +SELECT pg_stat_statements_reset(); + pg_stat_statements_reset +-------------------------- + +(1 row) + +-- +-- Track the total number of rows retrieved or affected by the utility +-- commands of COPY, FETCH, CREATE TABLE AS, CREATE MATERIALIZED VIEW, +-- REFRESH MATERIALIZED VIEW and SELECT INTO +-- +CREATE TABLE pgss_ctas AS SELECT a, 'ctas' b FROM generate_series(1, 10) a; +SELECT generate_series(1, 10) c INTO pgss_select_into; +COPY pgss_ctas (a, b) FROM STDIN; +CREATE MATERIALIZED VIEW pgss_matv AS SELECT * FROM pgss_ctas; +REFRESH MATERIALIZED VIEW pgss_matv; +BEGIN; +DECLARE pgss_cursor CURSOR FOR SELECT * FROM pgss_matv; +FETCH NEXT pgss_cursor; + a | b +---+------ + 1 | ctas +(1 row) + +FETCH FORWARD 5 pgss_cursor; + a | b +---+------ + 2 | ctas + 3 | ctas + 4 | ctas + 5 | ctas + 6 | ctas +(5 rows) + +FETCH FORWARD ALL pgss_cursor; + a | b +----+------ + 7 | ctas + 8 | ctas + 9 | ctas + 10 | ctas + 11 | copy + 12 | copy + 13 | copy +(7 rows) + +COMMIT; +SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C"; + calls | rows | query +-------+------+---------------------------------------------------------------------------- + 1 | 0 | BEGIN + 1 | 0 | COMMIT + 1 | 3 | COPY pgss_ctas (a, b) FROM STDIN + 1 | 13 | CREATE MATERIALIZED VIEW pgss_matv AS SELECT * FROM pgss_ctas + 1 | 10 | CREATE TABLE pgss_ctas AS SELECT a, 'ctas' b FROM generate_series(1, 10) a + 1 | 0 | DECLARE pgss_cursor CURSOR FOR SELECT * FROM pgss_matv + 1 | 5 | FETCH FORWARD 5 pgss_cursor + 1 | 7 | FETCH FORWARD ALL pgss_cursor + 1 | 1 | FETCH NEXT pgss_cursor + 1 | 13 | REFRESH MATERIALIZED VIEW pgss_matv + 1 | 10 | SELECT generate_series(1, 10) c INTO pgss_select_into + 1 | 1 | SELECT pg_stat_statements_reset() +(12 rows) + +DROP MATERIALIZED VIEW pgss_matv; +DROP TABLE pgss_ctas; +DROP TABLE pgss_select_into; +SELECT pg_stat_statements_reset(); + pg_stat_statements_reset +-------------------------- + +(1 row) + +-- SET statements. +-- These use two different strings, still they count as one entry. +SET work_mem = '1MB'; +Set work_mem = '1MB'; +SET work_mem = '2MB'; +RESET work_mem; +SET enable_seqscan = off; +SET enable_seqscan = on; +RESET enable_seqscan; +SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C"; + calls | rows | query +-------+------+----------------------------------- + 1 | 0 | RESET enable_seqscan + 1 | 0 | RESET work_mem + 1 | 1 | SELECT pg_stat_statements_reset() + 1 | 0 | SET enable_seqscan = off + 1 | 0 | SET enable_seqscan = on + 2 | 0 | SET work_mem = '1MB' + 1 | 0 | SET work_mem = '2MB' +(7 rows) + diff --git a/contrib/pg_stat_statements/meson.build b/contrib/pg_stat_statements/meson.build index 508b53b4a27..93f8b0b3ff8 100644 --- a/contrib/pg_stat_statements/meson.build +++ b/contrib/pg_stat_statements/meson.build @@ -41,6 +41,10 @@ tests += { 'regress': { 'sql': [ 'pg_stat_statements', + 'utility', + 'level_tracking', + 'planning', + 'cleanup', 'oldextversions', ], 'regress_args': ['--temp-config', files('pg_stat_statements.conf')], diff --git a/contrib/pg_stat_statements/sql/cleanup.sql b/contrib/pg_stat_statements/sql/cleanup.sql new file mode 100644 index 00000000000..36bec35c406 --- /dev/null +++ b/contrib/pg_stat_statements/sql/cleanup.sql @@ -0,0 +1 @@ +DROP EXTENSION pg_stat_statements; diff --git a/contrib/pg_stat_statements/sql/level_tracking.sql b/contrib/pg_stat_statements/sql/level_tracking.sql new file mode 100644 index 00000000000..2f95bf98e5c --- /dev/null +++ b/contrib/pg_stat_statements/sql/level_tracking.sql @@ -0,0 +1,99 @@ +-- +-- Statement level tracking +-- + +SET pg_stat_statements.track_utility = TRUE; +SELECT pg_stat_statements_reset(); + +-- DO block - top-level tracking. +CREATE TABLE stats_track_tab (x int); +SET pg_stat_statements.track = 'top'; +DELETE FROM stats_track_tab; +DO $$ +BEGIN + DELETE FROM stats_track_tab; +END; +$$ LANGUAGE plpgsql; +SELECT toplevel, calls, query FROM pg_stat_statements + WHERE query LIKE '%DELETE%' ORDER BY query COLLATE "C", toplevel; +SELECT pg_stat_statements_reset(); + +-- DO block - all-level tracking. +SET pg_stat_statements.track = 'all'; +DELETE FROM stats_track_tab; +DO $$ +BEGIN + DELETE FROM stats_track_tab; +END; $$; +DO LANGUAGE plpgsql $$ +BEGIN + -- this is a SELECT + PERFORM 'hello world'::TEXT; +END; $$; +SELECT toplevel, calls, query FROM pg_stat_statements + ORDER BY query COLLATE "C", toplevel; + +-- PL/pgSQL function - top-level tracking. +SET pg_stat_statements.track = 'top'; +SET pg_stat_statements.track_utility = FALSE; +SELECT pg_stat_statements_reset(); +CREATE FUNCTION PLUS_TWO(i INTEGER) RETURNS INTEGER AS $$ +DECLARE + r INTEGER; +BEGIN + SELECT (i + 1 + 1.0)::INTEGER INTO r; + RETURN r; +END; $$ LANGUAGE plpgsql; + +SELECT PLUS_TWO(3); +SELECT PLUS_TWO(7); + +-- SQL function --- use LIMIT to keep it from being inlined +CREATE FUNCTION PLUS_ONE(i INTEGER) RETURNS INTEGER AS +$$ SELECT (i + 1.0)::INTEGER LIMIT 1 $$ LANGUAGE SQL; + +SELECT PLUS_ONE(8); +SELECT PLUS_ONE(10); + +SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C"; + +-- PL/pgSQL function - all-level tracking. +SET pg_stat_statements.track = 'all'; +SELECT pg_stat_statements_reset(); + +-- we drop and recreate the functions to avoid any caching funnies +DROP FUNCTION PLUS_ONE(INTEGER); +DROP FUNCTION PLUS_TWO(INTEGER); + +-- PL/pgSQL function +CREATE FUNCTION PLUS_TWO(i INTEGER) RETURNS INTEGER AS $$ +DECLARE + r INTEGER; +BEGIN + SELECT (i + 1 + 1.0)::INTEGER INTO r; + RETURN r; +END; $$ LANGUAGE plpgsql; + +SELECT PLUS_TWO(-1); +SELECT PLUS_TWO(2); + +-- SQL function --- use LIMIT to keep it from being inlined +CREATE FUNCTION PLUS_ONE(i INTEGER) RETURNS INTEGER AS +$$ SELECT (i + 1.0)::INTEGER LIMIT 1 $$ LANGUAGE SQL; + +SELECT PLUS_ONE(3); +SELECT PLUS_ONE(1); + +SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C"; +DROP FUNCTION PLUS_ONE(INTEGER); + +-- +-- pg_stat_statements.track = none +-- +SET pg_stat_statements.track = 'none'; +SELECT pg_stat_statements_reset(); + +SELECT 1 AS "one"; +SELECT 1 + 1 AS "two"; + +SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C"; diff --git a/contrib/pg_stat_statements/sql/pg_stat_statements.sql b/contrib/pg_stat_statements/sql/pg_stat_statements.sql index cebde7392b1..3a3d2350667 100644 --- a/contrib/pg_stat_statements/sql/pg_stat_statements.sql +++ b/contrib/pg_stat_statements/sql/pg_stat_statements.sql @@ -136,9 +136,7 @@ INSERT INTO pgss_test VALUES(generate_series(1, 10), 'aaa'); UPDATE pgss_test SET b = 'bbb' WHERE a > 7; DELETE FROM pgss_test WHERE a > 9; -- DROP test table -SET pg_stat_statements.track_utility = TRUE; DROP TABLE pgss_test; -SET pg_stat_statements.track_utility = FALSE; -- Check WAL is generated for the above statements SELECT query, calls, rows, @@ -147,82 +145,6 @@ wal_records > 0 as wal_records_generated, wal_records >= rows as wal_records_ge_rows FROM pg_stat_statements ORDER BY query COLLATE "C"; --- --- pg_stat_statements.track = none --- -SET pg_stat_statements.track = 'none'; -SELECT pg_stat_statements_reset(); - -SELECT 1 AS "one"; -SELECT 1 + 1 AS "two"; - -SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C"; - --- --- pg_stat_statements.track = top --- -SET pg_stat_statements.track = 'top'; -SELECT pg_stat_statements_reset(); - -DO LANGUAGE plpgsql $$ -BEGIN - -- this is a SELECT - PERFORM 'hello world'::TEXT; -END; -$$; - --- PL/pgSQL function -CREATE FUNCTION PLUS_TWO(i INTEGER) RETURNS INTEGER AS $$ -DECLARE - r INTEGER; -BEGIN - SELECT (i + 1 + 1.0)::INTEGER INTO r; - RETURN r; -END; $$ LANGUAGE plpgsql; - -SELECT PLUS_TWO(3); -SELECT PLUS_TWO(7); - --- SQL function --- use LIMIT to keep it from being inlined -CREATE FUNCTION PLUS_ONE(i INTEGER) RETURNS INTEGER AS -$$ SELECT (i + 1.0)::INTEGER LIMIT 1 $$ LANGUAGE SQL; - -SELECT PLUS_ONE(8); -SELECT PLUS_ONE(10); - -SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C"; - --- --- pg_stat_statements.track = all --- -SET pg_stat_statements.track = 'all'; -SELECT pg_stat_statements_reset(); - --- we drop and recreate the functions to avoid any caching funnies -DROP FUNCTION PLUS_ONE(INTEGER); -DROP FUNCTION PLUS_TWO(INTEGER); - --- PL/pgSQL function -CREATE FUNCTION PLUS_TWO(i INTEGER) RETURNS INTEGER AS $$ -DECLARE - r INTEGER; -BEGIN - SELECT (i + 1 + 1.0)::INTEGER INTO r; - RETURN r; -END; $$ LANGUAGE plpgsql; - -SELECT PLUS_TWO(-1); -SELECT PLUS_TWO(2); - --- SQL function --- use LIMIT to keep it from being inlined -CREATE FUNCTION PLUS_ONE(i INTEGER) RETURNS INTEGER AS -$$ SELECT (i + 1.0)::INTEGER LIMIT 1 $$ LANGUAGE SQL; - -SELECT PLUS_ONE(3); -SELECT PLUS_ONE(1); - -SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C"; - -- -- queries with locking clauses -- @@ -254,61 +176,10 @@ SELECT calls, query FROM pg_stat_statements ORDER BY query COLLATE "C"; DROP TABLE pgss_a, pgss_b CASCADE; --- --- utility commands --- -SET pg_stat_statements.track_utility = TRUE; -SELECT pg_stat_statements_reset(); - -SELECT 1; -CREATE INDEX test_b ON test(b); -DROP TABLE test \; -DROP TABLE IF EXISTS test \; -DROP FUNCTION PLUS_ONE(INTEGER); --- This DROP query uses two different strings, still they count as one entry. -DROP TABLE IF EXISTS test \; -Drop Table If Exists test \; -DROP FUNCTION IF EXISTS PLUS_ONE(INTEGER); -DROP FUNCTION PLUS_TWO(INTEGER); --- This SET query uses two different strings, still they count as one entry. -SET work_mem = '1MB'; -Set work_mem = '1MB'; -SET work_mem = '2MB'; -RESET work_mem; -SET enable_seqscan = off; -SET enable_seqscan = on; -RESET enable_seqscan; - -SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C"; - --- --- Track the total number of rows retrieved or affected by the utility --- commands of COPY, FETCH, CREATE TABLE AS, CREATE MATERIALIZED VIEW, --- REFRESH MATERIALIZED VIEW and SELECT INTO --- -SELECT pg_stat_statements_reset(); - -CREATE TABLE pgss_ctas AS SELECT a, 'ctas' b FROM generate_series(1, 10) a; -SELECT generate_series(1, 10) c INTO pgss_select_into; -COPY pgss_ctas (a, b) FROM STDIN; -11 copy -12 copy -13 copy -\. -CREATE MATERIALIZED VIEW pgss_matv AS SELECT * FROM pgss_ctas; -REFRESH MATERIALIZED VIEW pgss_matv; -BEGIN; -DECLARE pgss_cursor CURSOR FOR SELECT * FROM pgss_matv; -FETCH NEXT pgss_cursor; -FETCH FORWARD 5 pgss_cursor; -FETCH FORWARD ALL pgss_cursor; -COMMIT; - -SELECT query, plans, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C"; - -- -- Track user activity and reset them -- +SET pg_stat_statements.track_utility = TRUE; SELECT pg_stat_statements_reset(); CREATE ROLE regress_stats_user1; CREATE ROLE regress_stats_user2; @@ -369,30 +240,6 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C"; -- DROP ROLE regress_stats_user1; DROP ROLE regress_stats_user2; -DROP MATERIALIZED VIEW pgss_matv; -DROP TABLE pgss_ctas; -DROP TABLE pgss_select_into; - --- --- [re]plan counting --- -SELECT pg_stat_statements_reset(); -CREATE TABLE test (); -PREPARE prep1 AS SELECT COUNT(*) FROM test; -EXECUTE prep1; -EXECUTE prep1; -EXECUTE prep1; -ALTER TABLE test ADD COLUMN x int; -EXECUTE prep1; -SELECT 42; -SELECT 42; -SELECT 42; -SELECT query, plans, calls, rows FROM pg_stat_statements - WHERE query NOT LIKE 'PREPARE%' ORDER BY query COLLATE "C"; --- for the prepared statement we expect at least one replan, but cache --- invalidations could force more -SELECT query, plans >= 2 AND plans <= calls AS plans_ok, calls, rows FROM pg_stat_statements - WHERE query LIKE 'PREPARE%' ORDER BY query COLLATE "C"; -- -- access to pg_stat_statements_info view @@ -400,27 +247,6 @@ SELECT query, plans >= 2 AND plans <= calls AS plans_ok, calls, rows FROM pg_sta SELECT pg_stat_statements_reset(); SELECT dealloc FROM pg_stat_statements_info; --- --- top level handling --- -SET pg_stat_statements.track = 'top'; -DELETE FROM test; -DO $$ -BEGIN - DELETE FROM test; -END; -$$ LANGUAGE plpgsql; -SELECT query, toplevel, plans, calls FROM pg_stat_statements WHERE query LIKE '%DELETE%' ORDER BY query COLLATE "C", toplevel; - -SET pg_stat_statements.track = 'all'; -DELETE FROM test; -DO $$ -BEGIN - DELETE FROM test; -END; -$$ LANGUAGE plpgsql; -SELECT query, toplevel, plans, calls FROM pg_stat_statements WHERE query LIKE '%DELETE%' ORDER BY query COLLATE "C", toplevel; - -- FROM [ONLY] CREATE TABLE tbl_inh(id integer); CREATE TABLE tbl_inh_1() INHERITS (tbl_inh); @@ -472,5 +298,3 @@ SELECT ( ) FROM (VALUES(6,7)) v3(e,f) GROUP BY ROLLUP(e,f); SELECT COUNT(*) FROM pg_stat_statements WHERE query LIKE '%SELECT GROUPING%'; - -DROP EXTENSION pg_stat_statements; diff --git a/contrib/pg_stat_statements/sql/planning.sql b/contrib/pg_stat_statements/sql/planning.sql new file mode 100644 index 00000000000..a59b9363c4b --- /dev/null +++ b/contrib/pg_stat_statements/sql/planning.sql @@ -0,0 +1,31 @@ +-- +-- Information related to planning +-- + +-- These tests require track_planning to be enabled. +SET pg_stat_statements.track_planning = TRUE; +SELECT pg_stat_statements_reset(); + +-- +-- [re]plan counting +-- +CREATE TABLE stats_plan_test (); +PREPARE prep1 AS SELECT COUNT(*) FROM stats_plan_test; +EXECUTE prep1; +EXECUTE prep1; +EXECUTE prep1; +ALTER TABLE stats_plan_test ADD COLUMN x int; +EXECUTE prep1; +SELECT 42; +SELECT 42; +SELECT 42; +SELECT plans, calls, rows, query FROM pg_stat_statements + WHERE query NOT LIKE 'PREPARE%' ORDER BY query COLLATE "C"; +-- for the prepared statement we expect at least one replan, but cache +-- invalidations could force more +SELECT plans >= 2 AND plans <= calls AS plans_ok, calls, rows, query FROM pg_stat_statements + WHERE query LIKE 'PREPARE%' ORDER BY query COLLATE "C"; + +-- Cleanup +DROP TABLE stats_plan_test; +SELECT pg_stat_statements_reset(); diff --git a/contrib/pg_stat_statements/sql/utility.sql b/contrib/pg_stat_statements/sql/utility.sql new file mode 100644 index 00000000000..8710b55cf9e --- /dev/null +++ b/contrib/pg_stat_statements/sql/utility.sql @@ -0,0 +1,60 @@ +-- +-- Utility commands +-- + +-- These tests require track_utility to be enabled. +SET pg_stat_statements.track_utility = TRUE; +SELECT pg_stat_statements_reset(); + +SELECT 1; +CREATE TEMP TABLE stats_util_tab (a int, b char(20)); +CREATE INDEX test_b ON stats_util_tab(b); +DROP TABLE stats_util_tab \; +DROP TABLE IF EXISTS stats_util_tab; +-- This DROP query uses two different strings, still they count as one entry. +DROP TABLE IF EXISTS stats_util_tab \; +Drop Table If Exists stats_util_tab; + +SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C"; +SELECT pg_stat_statements_reset(); + +-- +-- Track the total number of rows retrieved or affected by the utility +-- commands of COPY, FETCH, CREATE TABLE AS, CREATE MATERIALIZED VIEW, +-- REFRESH MATERIALIZED VIEW and SELECT INTO +-- +CREATE TABLE pgss_ctas AS SELECT a, 'ctas' b FROM generate_series(1, 10) a; +SELECT generate_series(1, 10) c INTO pgss_select_into; +COPY pgss_ctas (a, b) FROM STDIN; +11 copy +12 copy +13 copy +\. +CREATE MATERIALIZED VIEW pgss_matv AS SELECT * FROM pgss_ctas; +REFRESH MATERIALIZED VIEW pgss_matv; +BEGIN; +DECLARE pgss_cursor CURSOR FOR SELECT * FROM pgss_matv; +FETCH NEXT pgss_cursor; +FETCH FORWARD 5 pgss_cursor; +FETCH FORWARD ALL pgss_cursor; +COMMIT; + +SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C"; + +DROP MATERIALIZED VIEW pgss_matv; +DROP TABLE pgss_ctas; +DROP TABLE pgss_select_into; + +SELECT pg_stat_statements_reset(); + +-- SET statements. +-- These use two different strings, still they count as one entry. +SET work_mem = '1MB'; +Set work_mem = '1MB'; +SET work_mem = '2MB'; +RESET work_mem; +SET enable_seqscan = off; +SET enable_seqscan = on; +RESET enable_seqscan; + +SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";