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; -}