From c622bf35a8d66b316d6ddb0137fdfc011f617117 Mon Sep 17 00:00:00 2001 From: Ibrar Ahmed Date: Mon, 24 Oct 2022 17:21:59 +0000 Subject: [PATCH] PG-174: Code cleanup. pg_stat_monitor is a bit longer; therefore, it requires some code cleanup. Therefore I decided to turn these tasks into multiple commits and PR to avoid various changes in one PR. This will ease the review and Q/A process. In this commit, I have done these tasks. 1 - Delete all the SQL.in files because these version-dependent files are becoming significant in quantity. Now added a single SQL file for which contains the dynamic SQL based on the PostgreSQL Version. 2 - New SQL files (pg_stat_monitor--2.0.sql) added for pg_stat_monitor version 2. 3 - A new SQL file (pg_stat_monitor--1.0--2.0.sql) is created, which will be used to upgrade from version 1.0 to 2.0. Currently, this file is empty. But whenever we add some API changes into 2.0, we need to update that file too. 4 - The control file (pg_stat_monitor.control) is updated for version 2.0. This change will make the CREATE EXTENSION default to pg_stat_monitor version 2.0 --- Makefile | 20 +- pg_stat_monitor--1.0--2.0.sql | 5 + pg_stat_monitor--1.0.13.sql.in | 266 ------------- ...-1.0.14.sql.in => pg_stat_monitor--1.0.sql | 363 +++++++++++------ pg_stat_monitor--1.0.sql.in | 255 ------------ ...-1.0.15.sql.in => pg_stat_monitor--2.0.sql | 365 ++++++++++++------ pg_stat_monitor.control | 2 +- pgsm_errors.c | 228 ----------- 8 files changed, 496 insertions(+), 1008 deletions(-) create mode 100644 pg_stat_monitor--1.0--2.0.sql delete mode 100644 pg_stat_monitor--1.0.13.sql.in rename pg_stat_monitor--1.0.14.sql.in => pg_stat_monitor--1.0.sql (56%) delete mode 100644 pg_stat_monitor--1.0.sql.in rename pg_stat_monitor--1.0.15.sql.in => pg_stat_monitor--2.0.sql (55%) delete mode 100644 pgsm_errors.c diff --git a/Makefile b/Makefile index 7a6f0b7..80d4eb8 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ MODULE_big = pg_stat_monitor OBJS = hash_query.o guc.o pg_stat_monitor.o $(WIN32RES) EXTENSION = pg_stat_monitor -DATA = pg_stat_monitor--1.0.sql +DATA = pg_stat_monitor--1.0.sql pg_stat_monitor--2.0.sql pg_stat_monitor--1.0--2.0.sql PGFILEDESC = "pg_stat_monitor - execution statistics of SQL statements" @@ -19,7 +19,6 @@ REGRESS = basic version guc counters relations database error_insert application # NO_INSTALLCHECK = 1 PG_CONFIG = pg_config -PGSM_INPUT_SQL_VERSION := 1.0 ifdef USE_PGXS MAJORVERSION := $(shell pg_config --version | awk {'print $$2'} | cut -f1 -d".") @@ -31,20 +30,3 @@ top_builddir = ../.. include $(top_builddir)/src/Makefile.global include $(top_srcdir)/contrib/contrib-global.mk endif - -ifeq ($(shell test $(MAJORVERSION) -gt 12; echo $$?),0) -PGSM_INPUT_SQL_VERSION := ${PGSM_INPUT_SQL_VERSION}.${MAJORVERSION} -endif - -$(info Using pg_stat_monitor--${PGSM_INPUT_SQL_VERSION}.sql.in file to generate sql filea.) - -ifneq (,$(wildcard ../pg_stat_monitor--${PGSM_INPUT_SQL_VERSION}.sql.in)) - CP := $(shell cp -v ../pg_stat_monitor--${PGSM_INPUT_SQL_VERSION}.sql.in ../pg_stat_monitor--1.0.sql) -endif -ifneq (,$(wildcard pg_stat_monitor--${PGSM_INPUT_SQL_VERSION}.sql.in)) - CP := $(shell cp -v pg_stat_monitor--${PGSM_INPUT_SQL_VERSION}.sql.in pg_stat_monitor--1.0.sql) -endif - -clean: - rm -rf ${DATA} - rm -rf t/results diff --git a/pg_stat_monitor--1.0--2.0.sql b/pg_stat_monitor--1.0--2.0.sql new file mode 100644 index 0000000..5f8cf1e --- /dev/null +++ b/pg_stat_monitor--1.0--2.0.sql @@ -0,0 +1,5 @@ +/* contrib/pg_stat_monitor/pg_stat_monitor--1.0--2.0.sql */ + +-- complain if script is sourced in psql, rather than via CREATE EXTENSION +\echo Use "ALTER EXTENSION pg_stat_monitor" to load this file. \quit + diff --git a/pg_stat_monitor--1.0.13.sql.in b/pg_stat_monitor--1.0.13.sql.in deleted file mode 100644 index c7c479c..0000000 --- a/pg_stat_monitor--1.0.13.sql.in +++ /dev/null @@ -1,266 +0,0 @@ -/* contrib/pg_stat_monitor/pg_stat_monitor--1.0.sql */ - --- complain if script is sourced in psql, rather than via CREATE EXTENSION -\echo Use "CREATE EXTENSION pg_stat_monitor" to load this file. \quit - --- Register functions. -CREATE FUNCTION pg_stat_monitor_reset() -RETURNS void -AS 'MODULE_PATHNAME' -LANGUAGE C PARALLEL SAFE; - -CREATE FUNCTION pg_stat_monitor_version() -RETURNS text -AS 'MODULE_PATHNAME' -LANGUAGE C PARALLEL SAFE; - -CREATE FUNCTION get_histogram_timings() -RETURNS text -AS 'MODULE_PATHNAME' -LANGUAGE C PARALLEL SAFE; - -CREATE FUNCTION range() -RETURNS text[] AS $$ -SELECT string_to_array(get_histogram_timings(), ','); -$$ LANGUAGE SQL; - -CREATE FUNCTION pg_stat_monitor_internal(IN showtext boolean, - OUT bucket int8, -- 0 - OUT userid oid, - OUT dbid oid, - OUT client_ip int8, - - OUT queryid text, -- 4 - OUT planid text, - OUT query text, - OUT query_plan text, - OUT state_code int8, - OUT top_queryid text, - OUT top_query text, - OUT application_name text, - - OUT relations text, -- 11 - OUT cmd_type int, - OUT elevel int, - OUT sqlcode TEXT, - OUT message text, - OUT bucket_start_time text, - - OUT calls int8, -- 16 - - OUT total_exec_time float8, - OUT min_exec_time float8, - OUT max_exec_time float8, - OUT mean_exec_time float8, - OUT stddev_exec_time float8, - - OUT rows_retrieved int8, - - OUT plans_calls int8, -- 23 - - OUT total_plan_time float8, - OUT min_plan_time float8, - OUT max_plan_time float8, - OUT mean_plan_time float8, - OUT stddev_plan_time float8, - - OUT shared_blks_hit int8, -- 29 - OUT shared_blks_read int8, - OUT shared_blks_dirtied int8, - OUT shared_blks_written int8, - OUT local_blks_hit int8, - OUT local_blks_read int8, - OUT local_blks_dirtied int8, - OUT local_blks_written int8, - OUT temp_blks_read int8, - OUT temp_blks_written int8, - OUT blk_read_time float8, - OUT blk_write_time float8, - OUT resp_calls text, -- 41 - OUT cpu_user_time float8, - OUT cpu_sys_time float8, - OUT wal_records int8, - OUT wal_fpi int8, - OUT wal_bytes numeric, - OUT comments TEXT, - OUT toplevel BOOLEAN -) -RETURNS SETOF record -AS 'MODULE_PATHNAME', 'pg_stat_monitor' -LANGUAGE C STRICT VOLATILE PARALLEL SAFE; - -CREATE FUNCTION get_state(state_code int8) RETURNS TEXT AS -$$ -SELECT - CASE - WHEN state_code = 0 THEN 'PARSING' - WHEN state_code = 1 THEN 'PLANNING' - WHEN state_code = 2 THEN 'ACTIVE' - WHEN state_code = 3 THEN 'FINISHED' - WHEN state_code = 4 THEN 'FINISHED WITH ERROR' - END -$$ -LANGUAGE SQL PARALLEL SAFE; - -CREATE FUNCTION get_cmd_type (cmd_type INTEGER) RETURNS TEXT AS -$$ -SELECT - CASE - WHEN cmd_type = 0 THEN '' - WHEN cmd_type = 1 THEN 'SELECT' - WHEN cmd_type = 2 THEN 'UPDATE' - WHEN cmd_type = 3 THEN 'INSERT' - WHEN cmd_type = 4 THEN 'DELETE' - WHEN cmd_type = 5 THEN 'UTILITY' - WHEN cmd_type = 6 THEN 'NOTHING' - END -$$ -LANGUAGE SQL PARALLEL SAFE; - -CREATE FUNCTION pg_stat_monitor_settings( - OUT name text, - OUT value text, - OUT default_value text, - OUT description text, - OUT minimum INTEGER, - OUT maximum INTEGER, - OUT options text, - OUT restart text -) -RETURNS SETOF record -AS 'MODULE_PATHNAME', 'pg_stat_monitor_settings' -LANGUAGE C STRICT VOLATILE PARALLEL SAFE; - -CREATE VIEW pg_stat_monitor_settings AS SELECT - name, - value, - default_value, - description, - minimum, - maximum, - options, - restart -FROM pg_stat_monitor_settings(); - --- Register a view on the function for ease of use. -CREATE VIEW pg_stat_monitor AS SELECT - bucket, - bucket_start_time AS bucket_start_time, - userid::regrole, - datname, - '0.0.0.0'::inet + client_ip AS client_ip, - queryid, - top_queryid, - query, - comments, - planid, - query_plan, - top_query, - application_name, - string_to_array(relations, ',') AS relations, - cmd_type, - get_cmd_type(cmd_type) AS cmd_type_text, - elevel, - sqlcode, - message, - calls, - total_exec_time, - min_exec_time, - max_exec_time, - mean_exec_time, - stddev_exec_time, - - rows_retrieved, - - plans_calls, - - total_plan_time, - min_plan_time, - max_plan_time, - mean_plan_time, - stddev_plan_time, - - shared_blks_hit, - shared_blks_read, - shared_blks_dirtied, - shared_blks_written, - local_blks_hit, - local_blks_read, - local_blks_dirtied, - local_blks_written, - temp_blks_read, - temp_blks_written, - blk_read_time, - blk_write_time, - (string_to_array(resp_calls, ',')) resp_calls, - cpu_user_time, - cpu_sys_time, - wal_records, - wal_fpi, - wal_bytes, - state_code, - get_state(state_code) as state -FROM pg_stat_monitor_internal(TRUE) p, pg_database d WHERE dbid = oid -ORDER BY bucket_start_time; - -CREATE FUNCTION decode_error_level(elevel int) -RETURNS text -AS -$$ -SELECT - CASE - WHEN elevel = 0 THEN '' - WHEN elevel = 10 THEN 'DEBUG5' - WHEN elevel = 11 THEN 'DEBUG4' - WHEN elevel = 12 THEN 'DEBUG3' - WHEN elevel = 13 THEN 'DEBUG2' - WHEN elevel = 14 THEN 'DEBUG1' - WHEN elevel = 15 THEN 'LOG' - WHEN elevel = 16 THEN 'LOG_SERVER_ONLY' - WHEN elevel = 17 THEN 'INFO' - WHEN elevel = 18 THEN 'NOTICE' - WHEN elevel = 19 THEN 'WARNING' - WHEN elevel = 20 THEN 'ERROR' - END -$$ -LANGUAGE SQL PARALLEL SAFE; - -CREATE FUNCTION histogram(_bucket int, _quryid text) -RETURNS SETOF RECORD AS $$ -DECLARE - rec record; -BEGIN -for rec in - with stat as (select queryid, bucket, unnest(range()) as range, unnest(resp_calls)::int freq from pg_stat_monitor) select range, freq, repeat('■', (freq::float / max(freq) over() * 30)::int) as bar from stat where queryid = _quryid and bucket = _bucket -loop -return next rec; -end loop; -END -$$ language plpgsql; - ---CREATE FUNCTION pg_stat_monitor_hook_stats( --- OUT hook text, --- OUT min_time float8, --- OUT max_time float8, --- OUT total_time float8, --- OUT ncalls int8 ---) ---RETURNS SETOF record ---AS 'MODULE_PATHNAME', 'pg_stat_monitor_hook_stats' ---LANGUAGE C STRICT VOLATILE PARALLEL SAFE; - ---CREATE VIEW pg_stat_monitor_hook_stats AS SELECT --- hook, --- min_time, --- max_time, --- total_time, --- total_time / greatest(ncalls, 1) as avg_time, --- ncalls, --- ROUND(CAST(total_time / greatest(sum(total_time) OVER(), 0.00000001) * 100 as numeric), 2)::text || '%' as load_comparison --- FROM pg_stat_monitor_hook_stats(); - -GRANT SELECT ON pg_stat_monitor TO PUBLIC; -GRANT SELECT ON pg_stat_monitor_settings TO PUBLIC; - --- Don't want this to be available to non-superusers. -REVOKE ALL ON FUNCTION pg_stat_monitor_reset() FROM PUBLIC; diff --git a/pg_stat_monitor--1.0.14.sql.in b/pg_stat_monitor--1.0.sql similarity index 56% rename from pg_stat_monitor--1.0.14.sql.in rename to pg_stat_monitor--1.0.sql index 042eb42..51873b4 100644 --- a/pg_stat_monitor--1.0.14.sql.in +++ b/pg_stat_monitor--1.0.sql @@ -24,7 +24,102 @@ RETURNS text[] AS $$ SELECT string_to_array(get_histogram_timings(), ','); $$ LANGUAGE SQL; -CREATE FUNCTION pg_stat_monitor_internal(IN showtext boolean, +-- Some generic utility function used internally. + +CREATE FUNCTION get_state(state_code int8) RETURNS TEXT AS +$$ +SELECT + CASE + WHEN state_code = 0 THEN 'PARSING' + WHEN state_code = 1 THEN 'PLANNING' + WHEN state_code = 2 THEN 'ACTIVE' + WHEN state_code = 3 THEN 'FINISHED' + WHEN state_code = 4 THEN 'FINISHED WITH ERROR' + END +$$ +LANGUAGE SQL PARALLEL SAFE; + +CREATE FUNCTION get_cmd_type (cmd_type INTEGER) RETURNS TEXT AS +$$ +SELECT + CASE + WHEN cmd_type = 0 THEN '' + WHEN cmd_type = 1 THEN 'SELECT' + WHEN cmd_type = 2 THEN 'UPDATE' + WHEN cmd_type = 3 THEN 'INSERT' + WHEN cmd_type = 4 THEN 'DELETE' + WHEN cmd_type = 5 THEN 'UTILITY' + WHEN cmd_type = 6 THEN 'NOTHING' + END +$$ +LANGUAGE SQL PARALLEL SAFE; + +CREATE FUNCTION pg_stat_monitor_settings( + OUT name text, + OUT value text, + OUT default_value text, + OUT description text, + OUT minimum INTEGER, + OUT maximum INTEGER, + OUT options text, + OUT restart text +) +RETURNS SETOF record +AS 'MODULE_PATHNAME', 'pg_stat_monitor_settings' +LANGUAGE C STRICT VOLATILE PARALLEL SAFE; + +CREATE VIEW pg_stat_monitor_settings AS SELECT + name, + value, + default_value, + description, + minimum, + maximum, + options, + restart +FROM pg_stat_monitor_settings(); + +CREATE FUNCTION decode_error_level(elevel int) +RETURNS text +AS +$$ +SELECT + CASE + WHEN elevel = 0 THEN '' + WHEN elevel = 10 THEN 'DEBUG5' + WHEN elevel = 11 THEN 'DEBUG4' + WHEN elevel = 12 THEN 'DEBUG3' + WHEN elevel = 13 THEN 'DEBUG2' + WHEN elevel = 14 THEN 'DEBUG1' + WHEN elevel = 15 THEN 'LOG' + WHEN elevel = 16 THEN 'LOG_SERVER_ONLY' + WHEN elevel = 17 THEN 'INFO' + WHEN elevel = 18 THEN 'NOTICE' + WHEN elevel = 19 THEN 'WARNING' + WHEN elevel = 20 THEN 'ERROR' + END +$$ +LANGUAGE SQL PARALLEL SAFE; + +CREATE FUNCTION histogram(_bucket int, _quryid text) +RETURNS SETOF RECORD AS $$ +DECLARE + rec record; +BEGIN + FOR rec IN + WITH stat AS (select queryid, bucket, unnest(range()) AS range, + unnest(resp_calls)::int freq FROM pg_stat_monitor) select range, + freq, repeat('■', (freq::float / max(freq) over() * 30)::int) AS bar + FROM stat WHERE queryid = _quryid and bucket = _bucket + LOOP + RETURN next rec; + END loop; +END +$$ language plpgsql; + +-- pg_stat_monitor internal function, must not call outside from this file. +CREATE FUNCTION pg_stat_monitor_internal( + IN showtext boolean, OUT bucket int8, -- 0 OUT userid oid, OUT dbid oid, @@ -89,60 +184,10 @@ RETURNS SETOF record AS 'MODULE_PATHNAME', 'pg_stat_monitor' LANGUAGE C STRICT VOLATILE PARALLEL SAFE; -CREATE FUNCTION get_state(state_code int8) RETURNS TEXT AS -$$ -SELECT - CASE - WHEN state_code = 0 THEN 'PARSING' - WHEN state_code = 1 THEN 'PLANNING' - WHEN state_code = 2 THEN 'ACTIVE' - WHEN state_code = 3 THEN 'FINISHED' - WHEN state_code = 4 THEN 'FINISHED WITH ERROR' - END -$$ -LANGUAGE SQL PARALLEL SAFE; - -CREATE FUNCTION get_cmd_type (cmd_type INTEGER) RETURNS TEXT AS -$$ -SELECT - CASE - WHEN cmd_type = 0 THEN '' - WHEN cmd_type = 1 THEN 'SELECT' - WHEN cmd_type = 2 THEN 'UPDATE' - WHEN cmd_type = 3 THEN 'INSERT' - WHEN cmd_type = 4 THEN 'DELETE' - WHEN cmd_type = 5 THEN 'UTILITY' - WHEN cmd_type = 6 THEN 'NOTHING' - END -$$ -LANGUAGE SQL PARALLEL SAFE; - -CREATE FUNCTION pg_stat_monitor_settings( - OUT name text, - OUT value text, - OUT default_value text, - OUT description text, - OUT minimum INTEGER, - OUT maximum INTEGER, - OUT options text, - OUT restart text -) -RETURNS SETOF record -AS 'MODULE_PATHNAME', 'pg_stat_monitor_settings' -LANGUAGE C STRICT VOLATILE PARALLEL SAFE; - -CREATE VIEW pg_stat_monitor_settings AS SELECT - name, - value, - default_value, - description, - minimum, - maximum, - options, - restart -FROM pg_stat_monitor_settings(); - -- Register a view on the function for ease of use. +CREATE FUNCTION pgsm_create_11_view() RETURNS INT AS +$$ +BEGIN CREATE VIEW pg_stat_monitor AS SELECT bucket, bucket_start_time AS bucket_start_time, @@ -170,17 +215,7 @@ CREATE VIEW pg_stat_monitor AS SELECT max_exec_time, mean_exec_time, stddev_exec_time, - rows_retrieved, - - plans_calls, - - total_plan_time, - min_plan_time, - max_plan_time, - mean_plan_time, - stddev_plan_time, - shared_blks_hit, shared_blks_read, shared_blks_dirtied, @@ -203,65 +238,155 @@ CREATE VIEW pg_stat_monitor AS SELECT get_state(state_code) as state FROM pg_stat_monitor_internal(TRUE) p, pg_database d WHERE dbid = oid ORDER BY bucket_start_time; +RETURN 0; +END; +$$ LANGUAGE plpgsql; -CREATE FUNCTION decode_error_level(elevel int) -RETURNS text -AS -$$ -SELECT - CASE - WHEN elevel = 0 THEN '' - WHEN elevel = 10 THEN 'DEBUG5' - WHEN elevel = 11 THEN 'DEBUG4' - WHEN elevel = 12 THEN 'DEBUG3' - WHEN elevel = 13 THEN 'DEBUG2' - WHEN elevel = 14 THEN 'DEBUG1' - WHEN elevel = 15 THEN 'LOG' - WHEN elevel = 16 THEN 'LOG_SERVER_ONLY' - WHEN elevel = 17 THEN 'INFO' - WHEN elevel = 18 THEN 'NOTICE' - WHEN elevel = 19 THEN 'WARNING' - WHEN elevel = 20 THEN 'ERROR' - END -$$ -LANGUAGE SQL PARALLEL SAFE; -CREATE FUNCTION histogram(_bucket int, _quryid text) -RETURNS SETOF RECORD AS $$ -DECLARE - rec record; +CREATE FUNCTION pgsm_create_13_view() RETURNS INT AS +$$ BEGIN -for rec in - with stat as (select queryid, bucket, unnest(range()) as range, unnest(resp_calls)::int freq from pg_stat_monitor) select range, freq, repeat('■', (freq::float / max(freq) over() * 30)::int) as bar from stat where queryid = _quryid and bucket = _bucket -loop -return next rec; -end loop; -END -$$ language plpgsql; +CREATE VIEW pg_stat_monitor AS SELECT + bucket, + bucket_start_time AS bucket_start_time, + userid::regrole, + datname, + '0.0.0.0'::inet + client_ip AS client_ip, + queryid, + toplevel, + top_queryid, + query, + comments, + planid, + query_plan, + top_query, + application_name, + string_to_array(relations, ',') AS relations, + cmd_type, + get_cmd_type(cmd_type) AS cmd_type_text, + elevel, + sqlcode, + message, + calls, + total_exec_time, + min_exec_time, + max_exec_time, + mean_exec_time, + stddev_exec_time, + rows_retrieved, + shared_blks_hit, + shared_blks_read, + shared_blks_dirtied, + shared_blks_written, + local_blks_hit, + local_blks_read, + local_blks_dirtied, + local_blks_written, + temp_blks_read, + temp_blks_written, + blk_read_time, + blk_write_time, + (string_to_array(resp_calls, ',')) resp_calls, + cpu_user_time, + cpu_sys_time, + wal_records, + wal_fpi, + wal_bytes, + state_code, + get_state(state_code) as state, ---CREATE FUNCTION pg_stat_monitor_hook_stats( --- OUT hook text, --- OUT min_time float8, --- OUT max_time float8, --- OUT total_time float8, --- OUT ncalls int8 ---) ---RETURNS SETOF record ---AS 'MODULE_PATHNAME', 'pg_stat_monitor_hook_stats' ---LANGUAGE C STRICT VOLATILE PARALLEL SAFE; --- ---CREATE VIEW pg_stat_monitor_hook_stats AS SELECT --- hook, --- min_time, --- max_time, --- total_time, --- total_time / greatest(ncalls, 1) as avg_time, --- ncalls, --- ROUND(CAST(total_time / greatest(sum(total_time) OVER(), 0.00000001) * 100 as numeric), 2)::text || '%' as load_comparison --- FROM pg_stat_monitor_hook_stats(); + -- PostgreSQL-13 Specific Coulumns + plans_calls +FROM pg_stat_monitor_internal(TRUE) p, pg_database d WHERE dbid = oid +ORDER BY bucket_start_time; +RETURN 0; +END; +$$ LANGUAGE plpgsql; + +CREATE FUNCTION pgsm_create_14_view() RETURNS INT AS +$$ +BEGIN +CREATE VIEW pg_stat_monitor AS SELECT + bucket, + bucket_start_time AS bucket_start_time, + userid::regrole, + datname, + '0.0.0.0'::inet + client_ip AS client_ip, + queryid, + toplevel, + top_queryid, + query, + comments, + planid, + query_plan, + top_query, + application_name, + string_to_array(relations, ',') AS relations, + cmd_type, + get_cmd_type(cmd_type) AS cmd_type_text, + elevel, + sqlcode, + message, + calls, + total_exec_time, + min_exec_time, + max_exec_time, + mean_exec_time, + stddev_exec_time, + rows_retrieved, + shared_blks_hit, + shared_blks_read, + shared_blks_dirtied, + shared_blks_written, + local_blks_hit, + local_blks_read, + local_blks_dirtied, + local_blks_written, + temp_blks_read, + temp_blks_written, + blk_read_time, + blk_write_time, + (string_to_array(resp_calls, ',')) resp_calls, + cpu_user_time, + cpu_sys_time, + wal_records, + wal_fpi, + wal_bytes, + state_code, + get_state(state_code) as state, + + -- PostgreSQL-14 Specific Columns + plans_calls, + total_plan_time, + min_plan_time, + max_plan_time, + mean_plan_time, + stddev_plan_time +FROM pg_stat_monitor_internal(TRUE) p, pg_database d WHERE dbid = oid +ORDER BY bucket_start_time; +RETURN 0; +END; +$$ LANGUAGE plpgsql; + +CREATE FUNCTION pgsm_create_view() RETURNS INT AS +$$ + DECLARE ver integer; + BEGIN + SELECT current_setting('server_version_num') INTO ver; + IF (ver >= 14000) THEN + return pgsm_create_14_view(); + END IF; + IF (ver >= 13000) THEN + return pgsm_create_13_view(); + END IF; + IF (ver >= 11000) THEN + return pgsm_create_11_view(); + END IF; + RETURN 0; + END; +$$ LANGUAGE plpgsql; + +SELECT pgsm_create_view(); GRANT SELECT ON pg_stat_monitor TO PUBLIC; -GRANT SELECT ON pg_stat_monitor_settings TO PUBLIC; --- Don't want this to be available to non-superusers. -REVOKE ALL ON FUNCTION pg_stat_monitor_reset() FROM PUBLIC; diff --git a/pg_stat_monitor--1.0.sql.in b/pg_stat_monitor--1.0.sql.in deleted file mode 100644 index 0a6c41f..0000000 --- a/pg_stat_monitor--1.0.sql.in +++ /dev/null @@ -1,255 +0,0 @@ -/* contrib/pg_stat_monitor/pg_stat_monitor--1.0.sql */ - --- complain if script is sourced in psql, rather than via CREATE EXTENSION -\echo Use "CREATE EXTENSION pg_stat_monitor" to load this file. \quit - --- Register functions. -CREATE FUNCTION pg_stat_monitor_reset() -RETURNS void -AS 'MODULE_PATHNAME' -LANGUAGE C PARALLEL SAFE; - -CREATE FUNCTION pg_stat_monitor_version() -RETURNS text -AS 'MODULE_PATHNAME' -LANGUAGE C PARALLEL SAFE; - -CREATE FUNCTION get_histogram_timings() -RETURNS text -AS 'MODULE_PATHNAME' -LANGUAGE C PARALLEL SAFE; - -CREATE FUNCTION range() -RETURNS text[] AS $$ -SELECT string_to_array(get_histogram_timings(), ','); -$$ LANGUAGE SQL; - -CREATE FUNCTION pg_stat_monitor_internal(IN showtext boolean, - OUT bucket int8, -- 0 - OUT userid oid, - OUT dbid oid, - OUT client_ip int8, - - OUT queryid text, -- 4 - OUT planid text, - OUT query text, - OUT query_plan text, - OUT state_code int8, - OUT top_queryid text, - OUT top_query text, - OUT application_name text, - - OUT relations text, -- 11 - OUT cmd_type int, - OUT elevel int, - OUT sqlcode TEXT, - OUT message text, - OUT bucket_start_time text, - - OUT calls int8, -- 16 - OUT total_time float8, - OUT min_time float8, - OUT max_time float8, - OUT mean_time float8, - OUT stddev_time float8, - OUT rows_retrieved int8, - - OUT plans_calls int8, -- 23 - OUT plan_total_time float8, - OUT plan_min_time float8, - OUT plan_max_time float8, - OUT plan_mean_time float8, - OUT plan_stddev_time float8, - - OUT shared_blks_hit int8, -- 29 - OUT shared_blks_read int8, - OUT shared_blks_dirtied int8, - OUT shared_blks_written int8, - OUT local_blks_hit int8, - OUT local_blks_read int8, - OUT local_blks_dirtied int8, - OUT local_blks_written int8, - OUT temp_blks_read int8, - OUT temp_blks_written int8, - OUT blk_read_time float8, - OUT blk_write_time float8, - OUT resp_calls text, -- 41 - OUT cpu_user_time float8, - OUT cpu_sys_time float8, - OUT wal_records int8, - OUT wal_fpi int8, - OUT wal_bytes numeric, - OUT comments TEXT, - OUT toplevel BOOLEAN - ) -RETURNS SETOF record -AS 'MODULE_PATHNAME', 'pg_stat_monitor' -LANGUAGE C STRICT VOLATILE PARALLEL SAFE; - -CREATE FUNCTION get_state(state_code int8) RETURNS TEXT AS -$$ -SELECT - CASE - WHEN state_code = 0 THEN 'PARSING' - WHEN state_code = 1 THEN 'PLANNING' - WHEN state_code = 2 THEN 'ACTIVE' - WHEN state_code = 3 THEN 'FINISHED' - WHEN state_code = 4 THEN 'FINISHED WITH ERROR' - END -$$ -LANGUAGE SQL PARALLEL SAFE; - -CREATE FUNCTION get_cmd_type (cmd_type INTEGER) RETURNS TEXT AS -$$ -SELECT - CASE - WHEN cmd_type = 0 THEN '' - WHEN cmd_type = 1 THEN 'SELECT' - WHEN cmd_type = 2 THEN 'UPDATE' - WHEN cmd_type = 3 THEN 'INSERT' - WHEN cmd_type = 4 THEN 'DELETE' - WHEN cmd_type = 5 THEN 'UTILITY' - WHEN cmd_type = 6 THEN 'NOTHING' - END -$$ -LANGUAGE SQL PARALLEL SAFE; - -CREATE FUNCTION pg_stat_monitor_settings( - OUT name text, - OUT value text, - OUT default_value text, - OUT description text, - OUT minimum INTEGER, - OUT maximum INTEGER, - OUT options text, - OUT restart text -) -RETURNS SETOF record -AS 'MODULE_PATHNAME', 'pg_stat_monitor_settings' -LANGUAGE C STRICT VOLATILE PARALLEL SAFE; - -CREATE VIEW pg_stat_monitor_settings AS SELECT - name, - value, - default_value, - description, - minimum, - maximum, - options, - restart -FROM pg_stat_monitor_settings(); - --- Register a view on the function for ease of use. -CREATE VIEW pg_stat_monitor AS SELECT - bucket, - bucket_start_time AS bucket_start_time, - userid::regrole, - datname, - '0.0.0.0'::inet + client_ip AS client_ip, - queryid, - top_queryid, - query, - comments, - planid, - query_plan, - top_query, - application_name, - string_to_array(relations, ',') AS relations, - cmd_type, - get_cmd_type(cmd_type) AS cmd_type_text, - elevel, - sqlcode, - message, - calls, - total_time, - min_time, - max_time, - mean_time, - stddev_time, - rows_retrieved, - shared_blks_hit, - shared_blks_read, - shared_blks_dirtied, - shared_blks_written, - local_blks_hit, - local_blks_read, - local_blks_dirtied, - local_blks_written, - temp_blks_read, - temp_blks_written, - blk_read_time, - blk_write_time, - (string_to_array(resp_calls, ',')) resp_calls, - cpu_user_time, - cpu_sys_time, - wal_records, - wal_fpi, - wal_bytes, - state_code, - get_state(state_code) as state -FROM pg_stat_monitor_internal(TRUE) p, pg_database d WHERE dbid = oid -ORDER BY bucket_start_time; - -CREATE FUNCTION decode_error_level(elevel int) -RETURNS text -AS -$$ -SELECT - CASE - WHEN elevel = 0 THEN '' - WHEN elevel = 10 THEN 'DEBUG5' - WHEN elevel = 11 THEN 'DEBUG4' - WHEN elevel = 12 THEN 'DEBUG3' - WHEN elevel = 13 THEN 'DEBUG2' - WHEN elevel = 14 THEN 'DEBUG1' - WHEN elevel = 15 THEN 'LOG' - WHEN elevel = 16 THEN 'LOG_SERVER_ONLY' - WHEN elevel = 17 THEN 'INFO' - WHEN elevel = 18 THEN 'NOTICE' - WHEN elevel = 19 THEN 'WARNING' - WHEN elevel = 20 THEN 'ERROR' - END -$$ -LANGUAGE SQL PARALLEL SAFE; - -CREATE FUNCTION histogram(_bucket int, _quryid text) -RETURNS SETOF RECORD AS $$ -DECLARE - rec record; -BEGIN -for rec in - with stat as (select queryid, bucket, unnest(range()) as range, unnest(resp_calls)::int freq from pg_stat_monitor) select range, freq, repeat('■', (freq::float / max(freq) over() * 30)::int) as bar from stat where queryid = _quryid and bucket = _bucket -loop -return next rec; -end loop; -END -$$ language plpgsql; - --- CREATE FUNCTION pg_stat_monitor_hook_stats( --- OUT hook text, --- OUT min_time float8, --- OUT max_time float8, --- OUT total_time float8, --- OUT ncalls int8 ---) ---RETURNS SETOF record ---AS 'MODULE_PATHNAME', 'pg_stat_monitor_hook_stats' ---LANGUAGE C STRICT VOLATILE PARALLEL SAFE; - ---CREATE VIEW pg_stat_monitor_hook_stats AS SELECT --- hook, --- min_time, --- max_time, --- total_time, --- total_time / greatest(ncalls, 1) as avg_time, --- ncalls, --- ROUND(CAST(total_time / greatest(sum(total_time) OVER(), 0.00000001) * 100 as numeric), 2)::text || '%' as load_comparison --- FROM pg_stat_monitor_hook_stats(); - -GRANT SELECT ON pg_stat_monitor TO PUBLIC; -GRANT SELECT ON pg_stat_monitor_settings TO PUBLIC; - --- Don't want this to be available to non-superusers. -REVOKE ALL ON FUNCTION pg_stat_monitor_reset() FROM PUBLIC; - - diff --git a/pg_stat_monitor--1.0.15.sql.in b/pg_stat_monitor--2.0.sql similarity index 55% rename from pg_stat_monitor--1.0.15.sql.in rename to pg_stat_monitor--2.0.sql index 042eb42..df06d63 100644 --- a/pg_stat_monitor--1.0.15.sql.in +++ b/pg_stat_monitor--2.0.sql @@ -1,4 +1,4 @@ -/* contrib/pg_stat_monitor/pg_stat_monitor--1.0.sql */ +/* contrib/pg_stat_monitor/pg_stat_monitor--2.0.sql */ -- complain if script is sourced in psql, rather than via CREATE EXTENSION \echo Use "CREATE EXTENSION pg_stat_monitor" to load this file. \quit @@ -24,7 +24,102 @@ RETURNS text[] AS $$ SELECT string_to_array(get_histogram_timings(), ','); $$ LANGUAGE SQL; -CREATE FUNCTION pg_stat_monitor_internal(IN showtext boolean, +-- Some generic utility function used internally. + +CREATE FUNCTION get_state(state_code int8) RETURNS TEXT AS +$$ +SELECT + CASE + WHEN state_code = 0 THEN 'PARSING' + WHEN state_code = 1 THEN 'PLANNING' + WHEN state_code = 2 THEN 'ACTIVE' + WHEN state_code = 3 THEN 'FINISHED' + WHEN state_code = 4 THEN 'FINISHED WITH ERROR' + END +$$ +LANGUAGE SQL PARALLEL SAFE; + +CREATE FUNCTION get_cmd_type (cmd_type INTEGER) RETURNS TEXT AS +$$ +SELECT + CASE + WHEN cmd_type = 0 THEN '' + WHEN cmd_type = 1 THEN 'SELECT' + WHEN cmd_type = 2 THEN 'UPDATE' + WHEN cmd_type = 3 THEN 'INSERT' + WHEN cmd_type = 4 THEN 'DELETE' + WHEN cmd_type = 5 THEN 'UTILITY' + WHEN cmd_type = 6 THEN 'NOTHING' + END +$$ +LANGUAGE SQL PARALLEL SAFE; + +CREATE FUNCTION pg_stat_monitor_settings( + OUT name text, + OUT value text, + OUT default_value text, + OUT description text, + OUT minimum INTEGER, + OUT maximum INTEGER, + OUT options text, + OUT restart text +) +RETURNS SETOF record +AS 'MODULE_PATHNAME', 'pg_stat_monitor_settings' +LANGUAGE C STRICT VOLATILE PARALLEL SAFE; + +CREATE VIEW pg_stat_monitor_settings AS SELECT + name, + value, + default_value, + description, + minimum, + maximum, + options, + restart +FROM pg_stat_monitor_settings(); + +CREATE FUNCTION decode_error_level(elevel int) +RETURNS text +AS +$$ +SELECT + CASE + WHEN elevel = 0 THEN '' + WHEN elevel = 10 THEN 'DEBUG5' + WHEN elevel = 11 THEN 'DEBUG4' + WHEN elevel = 12 THEN 'DEBUG3' + WHEN elevel = 13 THEN 'DEBUG2' + WHEN elevel = 14 THEN 'DEBUG1' + WHEN elevel = 15 THEN 'LOG' + WHEN elevel = 16 THEN 'LOG_SERVER_ONLY' + WHEN elevel = 17 THEN 'INFO' + WHEN elevel = 18 THEN 'NOTICE' + WHEN elevel = 19 THEN 'WARNING' + WHEN elevel = 20 THEN 'ERROR' + END +$$ +LANGUAGE SQL PARALLEL SAFE; + +CREATE FUNCTION histogram(_bucket int, _quryid text) +RETURNS SETOF RECORD AS $$ +DECLARE + rec record; +BEGIN + FOR rec IN + WITH stat AS (select queryid, bucket, unnest(range()) AS range, + unnest(resp_calls)::int freq FROM pg_stat_monitor) select range, + freq, repeat('■', (freq::float / max(freq) over() * 30)::int) AS bar + FROM stat WHERE queryid = _quryid and bucket = _bucket + LOOP + RETURN next rec; + END loop; +END +$$ language plpgsql; + +-- pg_stat_monitor internal function, must not call outside from this file. +CREATE FUNCTION pg_stat_monitor_internal( + IN showtext boolean, OUT bucket int8, -- 0 OUT userid oid, OUT dbid oid, @@ -89,60 +184,10 @@ RETURNS SETOF record AS 'MODULE_PATHNAME', 'pg_stat_monitor' LANGUAGE C STRICT VOLATILE PARALLEL SAFE; -CREATE FUNCTION get_state(state_code int8) RETURNS TEXT AS -$$ -SELECT - CASE - WHEN state_code = 0 THEN 'PARSING' - WHEN state_code = 1 THEN 'PLANNING' - WHEN state_code = 2 THEN 'ACTIVE' - WHEN state_code = 3 THEN 'FINISHED' - WHEN state_code = 4 THEN 'FINISHED WITH ERROR' - END -$$ -LANGUAGE SQL PARALLEL SAFE; - -CREATE FUNCTION get_cmd_type (cmd_type INTEGER) RETURNS TEXT AS -$$ -SELECT - CASE - WHEN cmd_type = 0 THEN '' - WHEN cmd_type = 1 THEN 'SELECT' - WHEN cmd_type = 2 THEN 'UPDATE' - WHEN cmd_type = 3 THEN 'INSERT' - WHEN cmd_type = 4 THEN 'DELETE' - WHEN cmd_type = 5 THEN 'UTILITY' - WHEN cmd_type = 6 THEN 'NOTHING' - END -$$ -LANGUAGE SQL PARALLEL SAFE; - -CREATE FUNCTION pg_stat_monitor_settings( - OUT name text, - OUT value text, - OUT default_value text, - OUT description text, - OUT minimum INTEGER, - OUT maximum INTEGER, - OUT options text, - OUT restart text -) -RETURNS SETOF record -AS 'MODULE_PATHNAME', 'pg_stat_monitor_settings' -LANGUAGE C STRICT VOLATILE PARALLEL SAFE; - -CREATE VIEW pg_stat_monitor_settings AS SELECT - name, - value, - default_value, - description, - minimum, - maximum, - options, - restart -FROM pg_stat_monitor_settings(); - -- Register a view on the function for ease of use. +CREATE FUNCTION pgsm_create_11_view() RETURNS INT AS +$$ +BEGIN CREATE VIEW pg_stat_monitor AS SELECT bucket, bucket_start_time AS bucket_start_time, @@ -170,17 +215,7 @@ CREATE VIEW pg_stat_monitor AS SELECT max_exec_time, mean_exec_time, stddev_exec_time, - rows_retrieved, - - plans_calls, - - total_plan_time, - min_plan_time, - max_plan_time, - mean_plan_time, - stddev_plan_time, - shared_blks_hit, shared_blks_read, shared_blks_dirtied, @@ -203,65 +238,155 @@ CREATE VIEW pg_stat_monitor AS SELECT get_state(state_code) as state FROM pg_stat_monitor_internal(TRUE) p, pg_database d WHERE dbid = oid ORDER BY bucket_start_time; +RETURN 0; +END; +$$ LANGUAGE plpgsql; -CREATE FUNCTION decode_error_level(elevel int) -RETURNS text -AS -$$ -SELECT - CASE - WHEN elevel = 0 THEN '' - WHEN elevel = 10 THEN 'DEBUG5' - WHEN elevel = 11 THEN 'DEBUG4' - WHEN elevel = 12 THEN 'DEBUG3' - WHEN elevel = 13 THEN 'DEBUG2' - WHEN elevel = 14 THEN 'DEBUG1' - WHEN elevel = 15 THEN 'LOG' - WHEN elevel = 16 THEN 'LOG_SERVER_ONLY' - WHEN elevel = 17 THEN 'INFO' - WHEN elevel = 18 THEN 'NOTICE' - WHEN elevel = 19 THEN 'WARNING' - WHEN elevel = 20 THEN 'ERROR' - END -$$ -LANGUAGE SQL PARALLEL SAFE; -CREATE FUNCTION histogram(_bucket int, _quryid text) -RETURNS SETOF RECORD AS $$ -DECLARE - rec record; +CREATE FUNCTION pgsm_create_13_view() RETURNS INT AS +$$ BEGIN -for rec in - with stat as (select queryid, bucket, unnest(range()) as range, unnest(resp_calls)::int freq from pg_stat_monitor) select range, freq, repeat('■', (freq::float / max(freq) over() * 30)::int) as bar from stat where queryid = _quryid and bucket = _bucket -loop -return next rec; -end loop; -END -$$ language plpgsql; +CREATE VIEW pg_stat_monitor AS SELECT + bucket, + bucket_start_time AS bucket_start_time, + userid::regrole, + datname, + '0.0.0.0'::inet + client_ip AS client_ip, + queryid, + toplevel, + top_queryid, + query, + comments, + planid, + query_plan, + top_query, + application_name, + string_to_array(relations, ',') AS relations, + cmd_type, + get_cmd_type(cmd_type) AS cmd_type_text, + elevel, + sqlcode, + message, + calls, + total_exec_time, + min_exec_time, + max_exec_time, + mean_exec_time, + stddev_exec_time, + rows_retrieved, + shared_blks_hit, + shared_blks_read, + shared_blks_dirtied, + shared_blks_written, + local_blks_hit, + local_blks_read, + local_blks_dirtied, + local_blks_written, + temp_blks_read, + temp_blks_written, + blk_read_time, + blk_write_time, + (string_to_array(resp_calls, ',')) resp_calls, + cpu_user_time, + cpu_sys_time, + wal_records, + wal_fpi, + wal_bytes, + state_code, + get_state(state_code) as state, ---CREATE FUNCTION pg_stat_monitor_hook_stats( --- OUT hook text, --- OUT min_time float8, --- OUT max_time float8, --- OUT total_time float8, --- OUT ncalls int8 ---) ---RETURNS SETOF record ---AS 'MODULE_PATHNAME', 'pg_stat_monitor_hook_stats' ---LANGUAGE C STRICT VOLATILE PARALLEL SAFE; --- ---CREATE VIEW pg_stat_monitor_hook_stats AS SELECT --- hook, --- min_time, --- max_time, --- total_time, --- total_time / greatest(ncalls, 1) as avg_time, --- ncalls, --- ROUND(CAST(total_time / greatest(sum(total_time) OVER(), 0.00000001) * 100 as numeric), 2)::text || '%' as load_comparison --- FROM pg_stat_monitor_hook_stats(); + -- PostgreSQL-13 Specific Coulumns + plans_calls +FROM pg_stat_monitor_internal(TRUE) p, pg_database d WHERE dbid = oid +ORDER BY bucket_start_time; +RETURN 0; +END; +$$ LANGUAGE plpgsql; + +CREATE FUNCTION pgsm_create_14_view() RETURNS INT AS +$$ +BEGIN +CREATE VIEW pg_stat_monitor AS SELECT + bucket, + bucket_start_time AS bucket_start_time, + userid::regrole, + datname, + '0.0.0.0'::inet + client_ip AS client_ip, + queryid, + toplevel, + top_queryid, + query, + comments, + planid, + query_plan, + top_query, + application_name, + string_to_array(relations, ',') AS relations, + cmd_type, + get_cmd_type(cmd_type) AS cmd_type_text, + elevel, + sqlcode, + message, + calls, + total_exec_time, + min_exec_time, + max_exec_time, + mean_exec_time, + stddev_exec_time, + rows_retrieved, + shared_blks_hit, + shared_blks_read, + shared_blks_dirtied, + shared_blks_written, + local_blks_hit, + local_blks_read, + local_blks_dirtied, + local_blks_written, + temp_blks_read, + temp_blks_written, + blk_read_time, + blk_write_time, + (string_to_array(resp_calls, ',')) resp_calls, + cpu_user_time, + cpu_sys_time, + wal_records, + wal_fpi, + wal_bytes, + state_code, + get_state(state_code) as state, + + -- PostgreSQL-14 Specific Columns + plans_calls, + total_plan_time, + min_plan_time, + max_plan_time, + mean_plan_time, + stddev_plan_time +FROM pg_stat_monitor_internal(TRUE) p, pg_database d WHERE dbid = oid +ORDER BY bucket_start_time; +RETURN 0; +END; +$$ LANGUAGE plpgsql; + +CREATE FUNCTION pgsm_create_view() RETURNS INT AS +$$ + DECLARE ver integer; + BEGIN + SELECT current_setting('server_version_num') INTO ver; + IF (ver >= 14000) THEN + return pgsm_create_14_view(); + END IF; + IF (ver >= 13000) THEN + return pgsm_create_13_view(); + END IF; + IF (ver >= 11000) THEN + return pgsm_create_11_view(); + END IF; + RETURN 0; + END; +$$ LANGUAGE plpgsql; + +SELECT pgsm_create_view(); GRANT SELECT ON pg_stat_monitor TO PUBLIC; -GRANT SELECT ON pg_stat_monitor_settings TO PUBLIC; --- Don't want this to be available to non-superusers. -REVOKE ALL ON FUNCTION pg_stat_monitor_reset() FROM PUBLIC; diff --git a/pg_stat_monitor.control b/pg_stat_monitor.control index 2b826e9..c765131 100644 --- a/pg_stat_monitor.control +++ b/pg_stat_monitor.control @@ -1,5 +1,5 @@ # pg_stat_monitor extension comment = 'The pg_stat_monitor is a PostgreSQL Query Performance Monitoring tool, based on PostgreSQL contrib module pg_stat_statements. pg_stat_monitor provides aggregated statistics, client information, plan details including plan, and histogram information.' -default_version = '1.0' +default_version = '2.0' module_pathname = '$libdir/pg_stat_monitor' relocatable = true diff --git a/pgsm_errors.c b/pgsm_errors.c deleted file mode 100644 index 2381195..0000000 --- a/pgsm_errors.c +++ /dev/null @@ -1,228 +0,0 @@ -/*------------------------------------------------------------------------- - * - * pgsm_errors.c - * Track pg_stat_monitor internal error messages. - * - * Copyright © 2021, Percona LLC and/or its affiliates - * - * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group - * - * Portions Copyright (c) 1994, The Regents of the University of California - * - * IDENTIFICATION - * contrib/pg_stat_monitor/pgsm_errors.c - * - *------------------------------------------------------------------------- - */ - -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "pg_stat_monitor.h" -#include "pgsm_errors.h" - - -PG_FUNCTION_INFO_V1(pg_stat_monitor_errors); -PG_FUNCTION_INFO_V1(pg_stat_monitor_reset_errors); - - -/* - * Maximum number of error messages tracked. - * This should be set to a sensible value in order to track - * the different type of errors that pg_stat_monitor may - * report, e.g. out of memory. - */ -#define PSGM_ERRORS_MAX 128 - -static HTAB *pgsm_errors_ht = NULL; - -void -psgm_errors_init(void) -{ - HASHCTL info; -#if PG_VERSION_NUM >= 140000 - int flags = HASH_ELEM | HASH_STRINGS; -#else - int flags = HASH_ELEM | HASH_BLOBS; -#endif - - - memset(&info, 0, sizeof(info)); - info.keysize = ERROR_MSG_MAX_LEN; - info.entrysize = sizeof(ErrorEntry); - pgsm_errors_ht = ShmemInitHash("pg_stat_monitor: errors hashtable", - PSGM_ERRORS_MAX, /* initial size */ - PSGM_ERRORS_MAX, /* maximum size */ - &info, - flags); -} - -size_t -pgsm_errors_size(void) -{ - return hash_estimate_size(PSGM_ERRORS_MAX, sizeof(ErrorEntry)); -} - -void -pgsm_log(PgsmLogSeverity severity, const char *format,...) -{ - char key[ERROR_MSG_MAX_LEN]; - ErrorEntry *entry; - bool found = false; - va_list ap; - int n; - struct timeval tv; - struct tm *lt; - pgssSharedState *pgss; - - va_start(ap, format); - n = vsnprintf(key, ERROR_MSG_MAX_LEN, format, ap); - va_end(ap); - - if (n < 0) - return; - - pgss = pgsm_get_ss(); - LWLockAcquire(pgss->errors_lock, LW_EXCLUSIVE); - - entry = (ErrorEntry *) hash_search(pgsm_errors_ht, key, HASH_ENTER_NULL, &found); - if (!entry) - { - LWLockRelease(pgss->errors_lock); - - /* - * We're out of memory, can't track this error message. - */ - return; - } - - if (!found) - { - entry->severity = severity; - entry->calls = 0; - } - - /* Update message timestamp. */ - gettimeofday(&tv, NULL); - lt = localtime(&tv.tv_sec); - snprintf(entry->time, sizeof(entry->time), - "%04d-%02d-%02d %02d:%02d:%02d", - lt->tm_year + 1900, - lt->tm_mon + 1, - lt->tm_mday, - lt->tm_hour, - lt->tm_min, - lt->tm_sec); - - entry->calls++; - - LWLockRelease(pgss->errors_lock); -} - -/* - * Clear all entries from the hash table. - */ -Datum -pg_stat_monitor_reset_errors(PG_FUNCTION_ARGS) -{ - HASH_SEQ_STATUS hash_seq; - ErrorEntry *entry; - pgssSharedState *pgss = pgsm_get_ss(); - - /* Safety check... */ - if (!IsSystemInitialized()) - ereport(ERROR, - (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - errmsg("pg_stat_monitor: must be loaded via shared_preload_libraries"))); - - LWLockAcquire(pgss->errors_lock, LW_EXCLUSIVE); - - hash_seq_init(&hash_seq, pgsm_errors_ht); - while ((entry = hash_seq_search(&hash_seq)) != NULL) - entry = hash_search(pgsm_errors_ht, &entry->message, HASH_REMOVE, NULL); - - LWLockRelease(pgss->errors_lock); - PG_RETURN_VOID(); -} - -/* - * Invoked when users query the view pg_stat_monitor_errors. - * This function creates tuples with error messages from data present in - * the hash table, then return the dataset to the caller. - */ -Datum -pg_stat_monitor_errors(PG_FUNCTION_ARGS) -{ - ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; - TupleDesc tupdesc; - Tuplestorestate *tupstore; - MemoryContext per_query_ctx; - MemoryContext oldcontext; - HASH_SEQ_STATUS hash_seq; - ErrorEntry *error_entry; - pgssSharedState *pgss = pgsm_get_ss(); - - /* Safety check... */ - if (!IsSystemInitialized()) - ereport(ERROR, - (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - errmsg("pg_stat_monitor: must be loaded via shared_preload_libraries"))); - - /* check to see if caller supports us returning a tuplestore */ - if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo)) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("pg_stat_monitor: set-valued function called in context that cannot accept a set"))); - - /* Switch into long-lived context to construct returned data structures */ - per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; - oldcontext = MemoryContextSwitchTo(per_query_ctx); - - /* Build a tuple descriptor for our result type */ - if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) - elog(ERROR, "pg_stat_monitor: return type must be a row type"); - - if (tupdesc->natts != 4) - elog(ERROR, "pg_stat_monitor: incorrect number of output arguments, required 3, found %d", tupdesc->natts); - - tupstore = tuplestore_begin_heap(true, false, work_mem); - rsinfo->returnMode = SFRM_Materialize; - rsinfo->setResult = tupstore; - rsinfo->setDesc = tupdesc; - - MemoryContextSwitchTo(oldcontext); - - LWLockAcquire(pgss->errors_lock, LW_SHARED); - - hash_seq_init(&hash_seq, pgsm_errors_ht); - while ((error_entry = hash_seq_search(&hash_seq)) != NULL) - { - Datum values[4]; - bool nulls[4]; - int i = 0; - - memset(values, 0, sizeof(values)); - memset(nulls, 0, sizeof(nulls)); - - values[i++] = Int64GetDatumFast(error_entry->severity); - values[i++] = CStringGetTextDatum(error_entry->message); - values[i++] = CStringGetTextDatum(error_entry->time); - values[i++] = Int64GetDatumFast(error_entry->calls); - - tuplestore_putvalues(tupstore, tupdesc, values, nulls); - } - - LWLockRelease(pgss->errors_lock); - /* clean up and return the tuplestore */ - tuplestore_donestoring(tupstore); - - return (Datum) 0; -}