Merge branch 'main' into PG-312
commit
d03fb8f0b7
|
@ -8,7 +8,7 @@ jobs:
|
|||
timeout-minutes: 30
|
||||
steps:
|
||||
- name: Clone QA Integration repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: 'Percona-Lab/qa-integration'
|
||||
ref: 'main'
|
||||
|
@ -16,9 +16,17 @@ jobs:
|
|||
# print branch and Repo name
|
||||
- name: Get branch and Repo Name
|
||||
run: echo 'The branch and Repo Name is' ${{ github.head_ref }} ${{ github.actor }}/pg_stat_monitor
|
||||
|
||||
- name: "Set TARGET_BRANCH variable for a PR run"
|
||||
if: github.event_name == 'pull_request'
|
||||
run: echo "TARGET_BRANCH=${{ github.event.pull_request.base.ref }}" >> $GITHUB_ENV
|
||||
|
||||
- name: "Set TARGET_BRANCH variable for a PUSH run"
|
||||
if: github.event_name == 'push'
|
||||
run: echo "TARGET_BRANCH=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
|
||||
|
||||
- name: Run PMM & PGSM Setup, E2E Tests
|
||||
run: bash -xe ./pmm_pgsm_setup/pmm_pgsm_setup.sh --pgsql-version=11 --pgstat-monitor-branch=REL_1_1_1
|
||||
run: bash -xe ./pmm_pgsm_setup/pmm_pgsm_setup.sh --pgsql-version=11 --pgstat-monitor-branch=${{ env.TARGET_BRANCH }}
|
||||
|
||||
- name: Get PMM-Agent Logs from the Container
|
||||
if: success() || failure() # run this step even if previous step failed
|
||||
|
|
|
@ -8,7 +8,7 @@ jobs:
|
|||
timeout-minutes: 30
|
||||
steps:
|
||||
- name: Clone QA Integration repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: 'Percona-Lab/qa-integration'
|
||||
ref: 'main'
|
||||
|
@ -16,9 +16,17 @@ jobs:
|
|||
# print branch and Repo name
|
||||
- name: Get branch and Repo Name
|
||||
run: echo 'The branch and Repo Name is' ${{ github.head_ref }} ${{ github.actor }}/pg_stat_monitor
|
||||
|
||||
- name: "Set TARGET_BRANCH variable for a PR run"
|
||||
if: github.event_name == 'pull_request'
|
||||
run: echo "TARGET_BRANCH=${{ github.event.pull_request.base.ref }}" >> $GITHUB_ENV
|
||||
|
||||
- name: "Set TARGET_BRANCH variable for a PUSH run"
|
||||
if: github.event_name == 'push'
|
||||
run: echo "TARGET_BRANCH=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
|
||||
|
||||
- name: Run PMM & PGSM Setup, E2E Tests
|
||||
run: bash -xe ./pmm_pgsm_setup/pmm_pgsm_setup.sh --pgsql-version=12 --pgstat-monitor-branch=REL_1_1_1
|
||||
run: bash -xe ./pmm_pgsm_setup/pmm_pgsm_setup.sh --pgsql-version=12 --pgstat-monitor-branch=${{ env.TARGET_BRANCH }}
|
||||
|
||||
- name: Get PMM-Agent Logs from the Container
|
||||
if: success() || failure() # run this step even if previous step failed
|
||||
|
|
|
@ -8,7 +8,7 @@ jobs:
|
|||
timeout-minutes: 30
|
||||
steps:
|
||||
- name: Clone QA Integration repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: 'Percona-Lab/qa-integration'
|
||||
ref: 'main'
|
||||
|
@ -17,8 +17,16 @@ jobs:
|
|||
- name: Get branch and Repo Name
|
||||
run: echo 'The branch and Repo Name is' ${{ github.head_ref }} ${{ github.actor }}/pg_stat_monitor
|
||||
|
||||
- name: "Set TARGET_BRANCH variable for a PR run"
|
||||
if: github.event_name == 'pull_request'
|
||||
run: echo "TARGET_BRANCH=${{ github.event.pull_request.base.ref }}" >> $GITHUB_ENV
|
||||
|
||||
- name: "Set TARGET_BRANCH variable for a PUSH run"
|
||||
if: github.event_name == 'push'
|
||||
run: echo "TARGET_BRANCH=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
|
||||
|
||||
- name: Run PMM & PGSM Setup, E2E Tests
|
||||
run: bash -xe ./pmm_pgsm_setup/pmm_pgsm_setup.sh --pgsql-version=13 --pgstat-monitor-branch=REL_1_1_1
|
||||
run: bash -xe ./pmm_pgsm_setup/pmm_pgsm_setup.sh --pgsql-version=13 --pgstat-monitor-branch=${{ env.TARGET_BRANCH }}
|
||||
|
||||
- name: Get PMM-Agent Logs from the Container
|
||||
if: success() || failure() # run this step even if previous step failed
|
||||
|
|
|
@ -8,7 +8,7 @@ jobs:
|
|||
timeout-minutes: 30
|
||||
steps:
|
||||
- name: Clone QA Integration repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: 'Percona-Lab/qa-integration'
|
||||
ref: 'main'
|
||||
|
@ -17,8 +17,16 @@ jobs:
|
|||
- name: Get branch and Repo Name
|
||||
run: echo 'The branch and Repo Name is' ${{ github.head_ref }} ${{ github.actor }}/pg_stat_monitor
|
||||
|
||||
- name: "Set TARGET_BRANCH variable for a PR run"
|
||||
if: github.event_name == 'pull_request'
|
||||
run: echo "TARGET_BRANCH=${{ github.event.pull_request.base.ref }}" >> $GITHUB_ENV
|
||||
|
||||
- name: "Set TARGET_BRANCH variable for a PUSH run"
|
||||
if: github.event_name == 'push'
|
||||
run: echo "TARGET_BRANCH=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
|
||||
|
||||
- name: Run PMM & PGSM Setup, E2E Tests
|
||||
run: bash -xe ./pmm_pgsm_setup/pmm_pgsm_setup.sh --pgsql-version=14 --pgstat-monitor-branch=REL_1_1_1
|
||||
run: bash -xe ./pmm_pgsm_setup/pmm_pgsm_setup.sh --pgsql-version=14 --pgstat-monitor-branch=${{ env.TARGET_BRANCH }}
|
||||
|
||||
- name: Get PMM-Agent Logs from the Container
|
||||
if: success() || failure() # run this step even if previous step failed
|
||||
|
|
|
@ -8,7 +8,7 @@ jobs:
|
|||
timeout-minutes: 30
|
||||
steps:
|
||||
- name: Clone QA Integration repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: 'Percona-Lab/qa-integration'
|
||||
ref: 'main'
|
||||
|
@ -17,8 +17,16 @@ jobs:
|
|||
- name: Get branch and Repo Name
|
||||
run: echo 'The branch and Repo Name is' ${{ github.head_ref }} ${{ github.actor }}/pg_stat_monitor
|
||||
|
||||
- name: "Set TARGET_BRANCH variable for a PR run"
|
||||
if: github.event_name == 'pull_request'
|
||||
run: echo "TARGET_BRANCH=${{ github.event.pull_request.base.ref }}" >> $GITHUB_ENV
|
||||
|
||||
- name: "Set TARGET_BRANCH variable for a PUSH run"
|
||||
if: github.event_name == 'push'
|
||||
run: echo "TARGET_BRANCH=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
|
||||
|
||||
- name: Run PMM & PGSM Setup, E2E Tests
|
||||
run: bash -xe ./pmm_pgsm_setup/pmm_pgsm_setup.sh --pgsql-version=15 --pgstat-monitor-branch=REL_1_1_1
|
||||
run: bash -xe ./pmm_pgsm_setup/pmm_pgsm_setup.sh --pgsql-version=15 --pgstat-monitor-branch=${{ env.TARGET_BRANCH }}
|
||||
|
||||
- name: Get PMM-Agent Logs from the Container
|
||||
if: success() || failure() # run this step even if previous step failed
|
||||
|
|
2
Makefile
2
Makefile
|
@ -12,7 +12,7 @@ LDFLAGS_SL += $(filter -lm, $(LIBS))
|
|||
|
||||
TAP_TESTS = 1
|
||||
REGRESS_OPTS = --temp-config $(top_srcdir)/contrib/pg_stat_monitor/pg_stat_monitor.conf --inputdir=regression
|
||||
REGRESS = basic version guc counters relations database error_insert application_name application_name_unique top_query cmd_type error rows tags
|
||||
REGRESS = basic version guc functions counters relations database error_insert application_name application_name_unique top_query cmd_type error rows tags
|
||||
|
||||
# Disabled because these tests require "shared_preload_libraries=pg_stat_statements",
|
||||
# which typical installcheck users do not have (e.g. buildfarm clients).
|
||||
|
|
|
@ -8,6 +8,7 @@ DROP FUNCTION pgsm_create_11_view CASCADE;
|
|||
DROP FUNCTION pgsm_create_13_view CASCADE;
|
||||
DROP FUNCTION pgsm_create_14_view CASCADE;
|
||||
DROP FUNCTION pgsm_create_view CASCADE;
|
||||
DROP FUNCTION pg_stat_monitor_settings CASCADE;
|
||||
|
||||
-- pg_stat_monitor internal function, must not call outside from this file.
|
||||
CREATE FUNCTION pg_stat_monitor_internal(
|
||||
|
@ -62,17 +63,31 @@ CREATE FUNCTION pg_stat_monitor_internal(
|
|||
OUT temp_blks_written int8,
|
||||
OUT blk_read_time float8,
|
||||
OUT blk_write_time float8,
|
||||
OUT temp_blk_read_time float8,
|
||||
OUT temp_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
|
||||
OUT wal_records int8,
|
||||
OUT wal_fpi int8,
|
||||
OUT wal_bytes numeric,
|
||||
OUT comments TEXT,
|
||||
|
||||
OUT jit_functions int8,
|
||||
OUT jit_generation_time float8,
|
||||
OUT jit_inlining_count int8,
|
||||
OUT jit_inlining_time float8,
|
||||
OUT jit_optimization_count int8,
|
||||
OUT jit_optimization_time float8,
|
||||
OUT jit_emission_count int8,
|
||||
OUT jit_emission_time float8,
|
||||
|
||||
OUT toplevel BOOLEAN,
|
||||
OUT bucket_done BOOLEAN
|
||||
)
|
||||
RETURNS SETOF record
|
||||
AS 'MODULE_PATHNAME', 'pg_stat_monitor'
|
||||
AS 'MODULE_PATHNAME', 'pg_stat_monitor_2_0'
|
||||
LANGUAGE C STRICT VOLATILE PARALLEL SAFE;
|
||||
|
||||
-- Register a view on the function for ease of use.
|
||||
|
@ -122,9 +137,7 @@ CREATE VIEW pg_stat_monitor AS SELECT
|
|||
(string_to_array(resp_calls, ',')) resp_calls,
|
||||
cpu_user_time,
|
||||
cpu_sys_time,
|
||||
wal_records,
|
||||
wal_fpi,
|
||||
wal_bytes
|
||||
bucket_done
|
||||
FROM pg_stat_monitor_internal(TRUE) p, pg_database d WHERE dbid = oid
|
||||
ORDER BY bucket_start_time;
|
||||
RETURN 0;
|
||||
|
@ -242,6 +255,7 @@ CREATE VIEW pg_stat_monitor AS SELECT
|
|||
wal_records,
|
||||
wal_fpi,
|
||||
wal_bytes,
|
||||
bucket_done,
|
||||
|
||||
plans_calls,
|
||||
total_plan_time,
|
||||
|
@ -255,18 +269,97 @@ RETURN 0;
|
|||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE FUNCTION pgsm_create_15_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,
|
||||
temp_blk_read_time,
|
||||
temp_blk_write_time,
|
||||
|
||||
(string_to_array(resp_calls, ',')) resp_calls,
|
||||
cpu_user_time,
|
||||
cpu_sys_time,
|
||||
wal_records,
|
||||
wal_fpi,
|
||||
wal_bytes,
|
||||
bucket_done,
|
||||
|
||||
plans_calls,
|
||||
total_plan_time,
|
||||
min_plan_time,
|
||||
max_plan_time,
|
||||
mean_plan_time,
|
||||
stddev_plan_time,
|
||||
|
||||
jit_functions,
|
||||
jit_generation_time,
|
||||
jit_inlining_count,
|
||||
jit_inlining_time,
|
||||
jit_optimization_count,
|
||||
jit_optimization_time,
|
||||
jit_emission_count,
|
||||
jit_emission_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
|
||||
IF (ver >= 150000) THEN
|
||||
return pgsm_create_15_view();
|
||||
END IF;
|
||||
IF (ver >= 140000) THEN
|
||||
return pgsm_create_14_view();
|
||||
END IF;
|
||||
IF (ver >= 13000) THEN
|
||||
IF (ver >= 130000) THEN
|
||||
return pgsm_create_13_view();
|
||||
END IF;
|
||||
IF (ver >= 11000) THEN
|
||||
IF (ver >= 110000) THEN
|
||||
return pgsm_create_11_view();
|
||||
END IF;
|
||||
RETURN 0;
|
||||
|
@ -274,11 +367,17 @@ $$
|
|||
$$ LANGUAGE plpgsql;
|
||||
|
||||
SELECT pgsm_create_view();
|
||||
|
||||
REVOKE ALL ON FUNCTION range FROM PUBLIC;
|
||||
REVOKE ALL ON FUNCTION get_cmd_type FROM PUBLIC;
|
||||
REVOKE ALL ON FUNCTION pg_stat_monitor_settings FROM PUBLIC;
|
||||
REVOKE ALL ON FUNCTION decode_error_level FROM PUBLIC;
|
||||
REVOKE ALL ON FUNCTION pg_stat_monitor_internal FROM PUBLIC;
|
||||
REVOKE ALL ON FUNCTION get_histogram_timings FROM PUBLIC;
|
||||
REVOKE ALL ON FUNCTION pgsm_create_view FROM PUBLIC;
|
||||
REVOKE ALL ON FUNCTION pgsm_create_11_view FROM PUBLIC;
|
||||
REVOKE ALL ON FUNCTION pgsm_create_13_view FROM PUBLIC;
|
||||
REVOKE ALL ON FUNCTION pgsm_create_14_view FROM PUBLIC;
|
||||
REVOKE ALL ON FUNCTION pgsm_create_15_view FROM PUBLIC;
|
||||
|
||||
GRANT SELECT ON pg_stat_monitor TO PUBLIC;
|
||||
|
||||
|
|
|
@ -178,7 +178,8 @@ CREATE FUNCTION pg_stat_monitor_internal(
|
|||
OUT wal_fpi int8,
|
||||
OUT wal_bytes numeric,
|
||||
OUT comments TEXT,
|
||||
OUT toplevel BOOLEAN
|
||||
OUT toplevel BOOLEAN,
|
||||
OUT bucket_done BOOLEAN
|
||||
)
|
||||
RETURNS SETOF record
|
||||
AS 'MODULE_PATHNAME', 'pg_stat_monitor'
|
||||
|
@ -396,6 +397,12 @@ REVOKE ALL ON FUNCTION get_cmd_type FROM PUBLIC;
|
|||
REVOKE ALL ON FUNCTION pg_stat_monitor_settings FROM PUBLIC;
|
||||
REVOKE ALL ON FUNCTION decode_error_level FROM PUBLIC;
|
||||
REVOKE ALL ON FUNCTION pg_stat_monitor_internal FROM PUBLIC;
|
||||
REVOKE ALL ON FUNCTION get_histogram_timings FROM PUBLIC;
|
||||
REVOKE ALL ON FUNCTION pgsm_create_view FROM PUBLIC;
|
||||
REVOKE ALL ON FUNCTION pgsm_create_11_view FROM PUBLIC;
|
||||
REVOKE ALL ON FUNCTION pgsm_create_13_view FROM PUBLIC;
|
||||
REVOKE ALL ON FUNCTION pgsm_create_14_view FROM PUBLIC;
|
||||
REVOKE ALL ON FUNCTION pgsm_create_15_view FROM PUBLIC;
|
||||
|
||||
GRANT SELECT ON pg_stat_monitor TO PUBLIC;
|
||||
|
||||
|
|
|
@ -41,31 +41,6 @@ SELECT
|
|||
$$
|
||||
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
|
||||
|
@ -116,7 +91,6 @@ CREATE FUNCTION pg_stat_monitor_internal(
|
|||
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,
|
||||
|
@ -126,7 +100,7 @@ CREATE FUNCTION pg_stat_monitor_internal(
|
|||
OUT elevel int,
|
||||
OUT sqlcode TEXT,
|
||||
OUT message text,
|
||||
OUT bucket_start_time timestamp,
|
||||
OUT bucket_start_time timestamptz,
|
||||
|
||||
OUT calls int8, -- 16
|
||||
|
||||
|
@ -158,17 +132,32 @@ CREATE FUNCTION pg_stat_monitor_internal(
|
|||
OUT temp_blks_written int8,
|
||||
OUT blk_read_time float8,
|
||||
OUT blk_write_time float8,
|
||||
|
||||
OUT temp_blk_read_time float8,
|
||||
OUT temp_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
|
||||
OUT wal_records int8,
|
||||
OUT wal_fpi int8,
|
||||
OUT wal_bytes numeric,
|
||||
OUT comments TEXT,
|
||||
|
||||
OUT jit_functions int8,
|
||||
OUT jit_generation_time float8,
|
||||
OUT jit_inlining_count int8,
|
||||
OUT jit_inlining_time float8,
|
||||
OUT jit_optimization_count int8,
|
||||
OUT jit_optimization_time float8,
|
||||
OUT jit_emission_count int8,
|
||||
OUT jit_emission_time float8,
|
||||
|
||||
OUT toplevel BOOLEAN,
|
||||
OUT bucket_done BOOLEAN
|
||||
)
|
||||
RETURNS SETOF record
|
||||
AS 'MODULE_PATHNAME', 'pg_stat_monitor'
|
||||
AS 'MODULE_PATHNAME', 'pg_stat_monitor_2_0'
|
||||
LANGUAGE C STRICT VOLATILE PARALLEL SAFE;
|
||||
|
||||
-- Register a view on the function for ease of use.
|
||||
|
@ -217,9 +206,7 @@ CREATE VIEW pg_stat_monitor AS SELECT
|
|||
(string_to_array(resp_calls, ',')) resp_calls,
|
||||
cpu_user_time,
|
||||
cpu_sys_time,
|
||||
wal_records,
|
||||
wal_fpi,
|
||||
wal_bytes
|
||||
bucket_done
|
||||
FROM pg_stat_monitor_internal(TRUE) p, pg_database d WHERE dbid = oid
|
||||
ORDER BY bucket_start_time;
|
||||
RETURN 0;
|
||||
|
@ -276,6 +263,7 @@ CREATE VIEW pg_stat_monitor AS SELECT
|
|||
wal_records,
|
||||
wal_fpi,
|
||||
wal_bytes,
|
||||
bucket_done,
|
||||
-- PostgreSQL-13 Specific Coulumns
|
||||
plans_calls,
|
||||
total_plan_time,
|
||||
|
@ -338,6 +326,7 @@ CREATE VIEW pg_stat_monitor AS SELECT
|
|||
wal_records,
|
||||
wal_fpi,
|
||||
wal_bytes,
|
||||
bucket_done,
|
||||
|
||||
plans_calls,
|
||||
total_plan_time,
|
||||
|
@ -351,11 +340,90 @@ RETURN 0;
|
|||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE FUNCTION pgsm_create_15_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,
|
||||
temp_blk_read_time,
|
||||
temp_blk_write_time,
|
||||
|
||||
(string_to_array(resp_calls, ',')) resp_calls,
|
||||
cpu_user_time,
|
||||
cpu_sys_time,
|
||||
wal_records,
|
||||
wal_fpi,
|
||||
wal_bytes,
|
||||
bucket_done,
|
||||
|
||||
plans_calls,
|
||||
total_plan_time,
|
||||
min_plan_time,
|
||||
max_plan_time,
|
||||
mean_plan_time,
|
||||
stddev_plan_time,
|
||||
|
||||
jit_functions,
|
||||
jit_generation_time,
|
||||
jit_inlining_count,
|
||||
jit_inlining_time,
|
||||
jit_optimization_count,
|
||||
jit_optimization_time,
|
||||
jit_emission_count,
|
||||
jit_emission_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 >= 150000) THEN
|
||||
return pgsm_create_15_view();
|
||||
END IF;
|
||||
IF (ver >= 140000) THEN
|
||||
return pgsm_create_14_view();
|
||||
END IF;
|
||||
|
@ -372,9 +440,14 @@ $$ LANGUAGE plpgsql;
|
|||
SELECT pgsm_create_view();
|
||||
REVOKE ALL ON FUNCTION range FROM PUBLIC;
|
||||
REVOKE ALL ON FUNCTION get_cmd_type FROM PUBLIC;
|
||||
REVOKE ALL ON FUNCTION pg_stat_monitor_settings FROM PUBLIC;
|
||||
REVOKE ALL ON FUNCTION decode_error_level FROM PUBLIC;
|
||||
REVOKE ALL ON FUNCTION pg_stat_monitor_internal FROM PUBLIC;
|
||||
REVOKE ALL ON FUNCTION get_histogram_timings FROM PUBLIC;
|
||||
REVOKE ALL ON FUNCTION pgsm_create_view FROM PUBLIC;
|
||||
REVOKE ALL ON FUNCTION pgsm_create_11_view FROM PUBLIC;
|
||||
REVOKE ALL ON FUNCTION pgsm_create_13_view FROM PUBLIC;
|
||||
REVOKE ALL ON FUNCTION pgsm_create_14_view FROM PUBLIC;
|
||||
REVOKE ALL ON FUNCTION pgsm_create_15_view FROM PUBLIC;
|
||||
|
||||
GRANT SELECT ON pg_stat_monitor TO PUBLIC;
|
||||
|
||||
|
|
|
@ -23,10 +23,25 @@
|
|||
#include "commands/explain.h"
|
||||
#include "pg_stat_monitor.h"
|
||||
|
||||
/*
|
||||
* Extension version number, for supporting older extension versions' objects
|
||||
*/
|
||||
typedef enum pgsmVersion
|
||||
{
|
||||
PGSM_V1_0 = 0,
|
||||
PGSM_V2_0
|
||||
} pgsmVersion;
|
||||
|
||||
|
||||
PG_MODULE_MAGIC;
|
||||
|
||||
#define BUILD_VERSION "1.1.1"
|
||||
#define PG_STAT_STATEMENTS_COLS 52 /* maximum of above */
|
||||
#define BUILD_VERSION "2.0.0-dev"
|
||||
|
||||
/* Number of output arguments (columns) for various API versions */
|
||||
#define PG_STAT_MONITOR_COLS_V1_0 52
|
||||
#define PG_STAT_MONITOR_COLS_V2_0 61
|
||||
#define PG_STAT_MONITOR_COLS 61 /* maximum of above */
|
||||
|
||||
#define PGSM_TEXT_FILE PGSTAT_STAT_PERMANENT_DIRECTORY "pg_stat_monitor_query"
|
||||
|
||||
#define roundf(x,d) ((floor(((x)*pow(10,d))+.5))/pow(10,d))
|
||||
|
@ -107,8 +122,9 @@ static ExecutorCheckPerms_hook_type prev_ExecutorCheckPerms_hook = NULL;
|
|||
|
||||
PG_FUNCTION_INFO_V1(pg_stat_monitor_version);
|
||||
PG_FUNCTION_INFO_V1(pg_stat_monitor_reset);
|
||||
PG_FUNCTION_INFO_V1(pg_stat_monitor_1_0);
|
||||
PG_FUNCTION_INFO_V1(pg_stat_monitor_2_0);
|
||||
PG_FUNCTION_INFO_V1(pg_stat_monitor);
|
||||
PG_FUNCTION_INFO_V1(pg_stat_monitor_settings);
|
||||
PG_FUNCTION_INFO_V1(get_histogram_timings);
|
||||
PG_FUNCTION_INFO_V1(pg_stat_monitor_hook_stats);
|
||||
|
||||
|
@ -174,10 +190,12 @@ static void pgss_store(uint64 queryid,
|
|||
uint64 rows,
|
||||
BufferUsage *bufusage,
|
||||
WalUsage *walusage,
|
||||
const struct JitInstrumentation *jitusage,
|
||||
JumbleState *jstate,
|
||||
pgssStoreKind kind);
|
||||
|
||||
static void pg_stat_monitor_internal(FunctionCallInfo fcinfo,
|
||||
pgsmVersion api_version,
|
||||
bool showtext);
|
||||
|
||||
#if PG_VERSION_NUM < 140000
|
||||
|
@ -228,7 +246,7 @@ _PG_init(void)
|
|||
* In order to create our shared memory area, we have to be loaded via
|
||||
* shared_preload_libraries. If not, fall out without hooking into any of
|
||||
* the main system. (We don't throw error here because it seems useful to
|
||||
* allow the pg_stat_statements functions to be created even when the
|
||||
* allow the pg_stat_monitor functions to be created even when the
|
||||
* module isn't active. The functions must protect themselves against
|
||||
* being called then, however.)
|
||||
*/
|
||||
|
@ -419,6 +437,7 @@ pgss_post_parse_analyze(ParseState *pstate, Query *query, JumbleState *jstate)
|
|||
0, /* rows */
|
||||
NULL, /* bufusage */
|
||||
NULL, /* walusage */
|
||||
NULL, /* jitusage */
|
||||
jstate, /* JumbleState */
|
||||
PGSS_PARSE); /* pgssStoreKind */
|
||||
}
|
||||
|
@ -477,6 +496,7 @@ pgss_post_parse_analyze(ParseState *pstate, Query *query)
|
|||
0, /* rows */
|
||||
NULL, /* bufusage */
|
||||
NULL, /* walusage */
|
||||
NULL, /* jitusage */
|
||||
&jstate, /* JumbleState */
|
||||
PGSS_PARSE); /* pgssStoreKind */
|
||||
}
|
||||
|
@ -648,6 +668,11 @@ pgss_ExecutorEnd(QueryDesc *queryDesc)
|
|||
&queryDesc->totaltime->walusage, /* walusage */
|
||||
#else
|
||||
NULL,
|
||||
#endif
|
||||
#if PG_VERSION_NUM >= 150000
|
||||
queryDesc->estate->es_jit ? &queryDesc->estate->es_jit->instr : NULL,
|
||||
#else
|
||||
NULL,
|
||||
#endif
|
||||
NULL,
|
||||
PGSS_FINISHED); /* pgssStoreKind */
|
||||
|
@ -791,6 +816,7 @@ pgss_planner_hook(Query *parse, const char *query_string, int cursorOptions, Par
|
|||
&bufusage, /* bufusage */
|
||||
&walusage, /* walusage */
|
||||
NULL, /* JumbleState */
|
||||
NULL,
|
||||
PGSS_PLAN); /* pgssStoreKind */
|
||||
}
|
||||
else
|
||||
|
@ -980,6 +1006,7 @@ pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
|
|||
#else
|
||||
NULL, /* walusage, NULL for PG <= 12 */
|
||||
#endif
|
||||
NULL,
|
||||
NULL, /* JumbleState */
|
||||
PGSS_FINISHED); /* pgssStoreKind */
|
||||
}
|
||||
|
@ -1126,7 +1153,7 @@ pg_get_client_addr(bool *ok)
|
|||
|
||||
static void
|
||||
pgss_update_entry(pgssEntry *entry,
|
||||
int bucketid,
|
||||
uint64 bucketid,
|
||||
uint64 queryid,
|
||||
const char *query,
|
||||
const char *comments,
|
||||
|
@ -1138,6 +1165,7 @@ pgss_update_entry(pgssEntry *entry,
|
|||
uint64 rows,
|
||||
BufferUsage *bufusage,
|
||||
WalUsage *walusage,
|
||||
const struct JitInstrumentation *jitusage,
|
||||
bool reset,
|
||||
pgssStoreKind kind,
|
||||
const char *app_name,
|
||||
|
@ -1259,6 +1287,10 @@ pgss_update_entry(pgssEntry *entry,
|
|||
e->counters.blocks.temp_blks_written += bufusage->temp_blks_written;
|
||||
e->counters.blocks.blk_read_time += INSTR_TIME_GET_MILLISEC(bufusage->blk_read_time);
|
||||
e->counters.blocks.blk_write_time += INSTR_TIME_GET_MILLISEC(bufusage->blk_write_time);
|
||||
#if PG_VERSION_NUM >= 150000
|
||||
e->counters.blocks.temp_blk_read_time += INSTR_TIME_GET_MILLISEC(bufusage->temp_blk_read_time);
|
||||
e->counters.blocks.temp_blk_write_time += INSTR_TIME_GET_MILLISEC(bufusage->temp_blk_write_time);
|
||||
#endif
|
||||
}
|
||||
e->counters.calls.usage += USAGE_EXEC(total_time);
|
||||
if (sys_info)
|
||||
|
@ -1272,6 +1304,23 @@ pgss_update_entry(pgssEntry *entry,
|
|||
e->counters.walusage.wal_fpi += walusage->wal_fpi;
|
||||
e->counters.walusage.wal_bytes += walusage->wal_bytes;
|
||||
}
|
||||
if (jitusage)
|
||||
{
|
||||
e->counters.jitinfo.jit_functions += jitusage->created_functions;
|
||||
e->counters.jitinfo.jit_generation_time += INSTR_TIME_GET_MILLISEC(jitusage->generation_counter);
|
||||
|
||||
if (INSTR_TIME_GET_MILLISEC(jitusage->inlining_counter))
|
||||
e->counters.jitinfo.jit_inlining_count++;
|
||||
e->counters.jitinfo.jit_inlining_time += INSTR_TIME_GET_MILLISEC(jitusage->inlining_counter);
|
||||
|
||||
if (INSTR_TIME_GET_MILLISEC(jitusage->optimization_counter))
|
||||
e->counters.jitinfo.jit_optimization_count++;
|
||||
e->counters.jitinfo.jit_optimization_time += INSTR_TIME_GET_MILLISEC(jitusage->optimization_counter);
|
||||
|
||||
if (INSTR_TIME_GET_MILLISEC(jitusage->emission_counter))
|
||||
e->counters.jitinfo.jit_emission_count++;
|
||||
e->counters.jitinfo.jit_emission_time += INSTR_TIME_GET_MILLISEC(jitusage->emission_counter);
|
||||
}
|
||||
SpinLockRelease(&e->mutex);
|
||||
}
|
||||
}
|
||||
|
@ -1300,6 +1349,7 @@ pgss_store_error(uint64 queryid,
|
|||
NULL, /* bufusage */
|
||||
NULL, /* walusage */
|
||||
NULL, /* JumbleState */
|
||||
NULL,
|
||||
PGSS_ERROR); /* pgssStoreKind */
|
||||
}
|
||||
|
||||
|
@ -1326,6 +1376,7 @@ pgss_store(uint64 queryid,
|
|||
uint64 rows,
|
||||
BufferUsage *bufusage,
|
||||
WalUsage *walusage,
|
||||
const struct JitInstrumentation *jitusage,
|
||||
JumbleState *jstate,
|
||||
pgssStoreKind kind)
|
||||
{
|
||||
|
@ -1540,6 +1591,7 @@ pgss_store(uint64 queryid,
|
|||
rows, /* rows */
|
||||
bufusage, /* bufusage */
|
||||
walusage, /* walusage */
|
||||
jitusage,
|
||||
reset, /* reset */
|
||||
kind, /* kind */
|
||||
app_name_ptr,
|
||||
|
@ -1573,25 +1625,39 @@ pg_stat_monitor_reset(PG_FUNCTION_ARGS)
|
|||
PG_RETURN_VOID();
|
||||
}
|
||||
|
||||
Datum
|
||||
pg_stat_monitor_1_0(PG_FUNCTION_ARGS)
|
||||
{
|
||||
pg_stat_monitor_internal(fcinfo, PGSM_V1_0, true);
|
||||
return (Datum) 0;
|
||||
}
|
||||
|
||||
Datum
|
||||
pg_stat_monitor_2_0(PG_FUNCTION_ARGS)
|
||||
{
|
||||
pg_stat_monitor_internal(fcinfo, PGSM_V2_0, true);
|
||||
return (Datum) 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Legacy entry point for pg_stat_monitor() API versions 1.0
|
||||
*/
|
||||
Datum
|
||||
pg_stat_monitor(PG_FUNCTION_ARGS)
|
||||
{
|
||||
pg_stat_monitor_internal(fcinfo, true);
|
||||
pg_stat_monitor_internal(fcinfo, PGSM_V1_0, true);
|
||||
return (Datum) 0;
|
||||
}
|
||||
|
||||
static bool
|
||||
IsBucketValid(uint64 bucketid)
|
||||
{
|
||||
struct tm tm;
|
||||
time_t bucket_t,
|
||||
current_t;
|
||||
double diff_t;
|
||||
pgssSharedState *pgss = pgsm_get_ss();
|
||||
|
||||
memset(&tm, 0, sizeof(tm));
|
||||
strptime(pgss->bucket_start_time[bucketid], "%Y-%m-%d %H:%M:%S", &tm);
|
||||
bucket_t = mktime(&tm);
|
||||
bucket_t = mktime(&pgss->bucket_start_time[bucketid]);
|
||||
|
||||
time(¤t_t);
|
||||
diff_t = difftime(current_t, bucket_t);
|
||||
|
@ -1600,9 +1666,10 @@ IsBucketValid(uint64 bucketid)
|
|||
return true;
|
||||
}
|
||||
|
||||
/* Common code for all versions of pg_stat_statements() */
|
||||
/* Common code for all versions of pg_stat_monitor() */
|
||||
static void
|
||||
pg_stat_monitor_internal(FunctionCallInfo fcinfo,
|
||||
pgsmVersion api_version,
|
||||
bool showtext)
|
||||
{
|
||||
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
|
||||
|
@ -1617,6 +1684,7 @@ pg_stat_monitor_internal(FunctionCallInfo fcinfo,
|
|||
HTAB *pgss_hash = pgsm_get_hash();
|
||||
char *query_txt = (char *) palloc0(PGSM_QUERY_MAX_LEN + 1);
|
||||
char *parent_query_txt = (char *) palloc0(PGSM_QUERY_MAX_LEN + 1);
|
||||
int expected_columns = (api_version >= PGSM_V2_0)?PG_STAT_MONITOR_COLS_V2_0:PG_STAT_MONITOR_COLS_V1_0;
|
||||
|
||||
/* Safety check... */
|
||||
if (!IsSystemInitialized())
|
||||
|
@ -1643,7 +1711,7 @@ pg_stat_monitor_internal(FunctionCallInfo fcinfo,
|
|||
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 != 51)
|
||||
if (tupdesc->natts != expected_columns)
|
||||
elog(ERROR, "pg_stat_monitor: incorrect number of output arguments, required %d", tupdesc->natts);
|
||||
|
||||
tupstore = tuplestore_begin_heap(true, false, work_mem);
|
||||
|
@ -1658,18 +1726,18 @@ pg_stat_monitor_internal(FunctionCallInfo fcinfo,
|
|||
hash_seq_init(&hash_seq, pgss_hash);
|
||||
while ((entry = hash_seq_search(&hash_seq)) != NULL)
|
||||
{
|
||||
Datum values[PG_STAT_STATEMENTS_COLS] = {0};
|
||||
bool nulls[PG_STAT_STATEMENTS_COLS] = {0};
|
||||
Datum values[PG_STAT_MONITOR_COLS] = {0};
|
||||
bool nulls[PG_STAT_MONITOR_COLS] = {0};
|
||||
int i = 0;
|
||||
Counters tmp;
|
||||
double stddev;
|
||||
char queryid_text[32] = {0};
|
||||
char planid_text[32] = {0};
|
||||
uint64 queryid = entry->key.queryid;
|
||||
uint64 bucketid = entry->key.bucket_id;
|
||||
int64 bucketid = entry->key.bucket_id;
|
||||
uint64 dbid = entry->key.dbid;
|
||||
uint64 userid = entry->key.userid;
|
||||
uint64 ip = entry->key.ip;
|
||||
int64 ip = entry->key.ip;
|
||||
uint64 planid = entry->key.planid;
|
||||
#if PG_VERSION_NUM < 140000
|
||||
bool toplevel = 1;
|
||||
|
@ -1718,7 +1786,7 @@ pg_stat_monitor_internal(FunctionCallInfo fcinfo,
|
|||
{
|
||||
if (read_query(pgss_qbuf, tmp.info.parentid, parent_query_txt, 0) == 0)
|
||||
{
|
||||
int rc = read_query_buffer(bucketid, tmp.info.parentid, parent_query_txt, 0);
|
||||
int rc = read_query_buffer(bucketid, tmp.info.parentid, parent_query_txt, 0);
|
||||
|
||||
if (rc != 1)
|
||||
snprintf(parent_query_txt, 32, "%s", "<insufficient disk/shared space>");
|
||||
|
@ -1788,8 +1856,9 @@ pg_stat_monitor_internal(FunctionCallInfo fcinfo,
|
|||
values[i++] = CStringGetTextDatum("<insufficient privilege>");
|
||||
}
|
||||
|
||||
/* state at column number 8 */
|
||||
values[i++] = Int64GetDatumFast(tmp.state);
|
||||
/* state at column number 8 for V1.0 API*/
|
||||
if (api_version <= PGSM_V1_0)
|
||||
values[i++] = Int64GetDatumFast(tmp.state);
|
||||
|
||||
/* parentid at column number 9 */
|
||||
if (tmp.info.parentid != UINT64CONST(0))
|
||||
|
@ -1843,7 +1912,7 @@ pg_stat_monitor_internal(FunctionCallInfo fcinfo,
|
|||
if (tmp.info.cmd_type == CMD_NOTHING)
|
||||
nulls[i++] = true;
|
||||
else
|
||||
values[i++] = Int64GetDatumFast(tmp.info.cmd_type);
|
||||
values[i++] = Int64GetDatumFast((int64)tmp.info.cmd_type);
|
||||
|
||||
/* elevel at column number 12 */
|
||||
values[i++] = Int64GetDatumFast(tmp.error.elevel);
|
||||
|
@ -1861,7 +1930,11 @@ pg_stat_monitor_internal(FunctionCallInfo fcinfo,
|
|||
values[i++] = CStringGetTextDatum(tmp.error.message);
|
||||
|
||||
/* bucket_start_time at column number 15 */
|
||||
values[i++] = CStringGetDatum(pgss->bucket_start_time[entry->key.bucket_id]);
|
||||
{
|
||||
TimestampTz tm;
|
||||
tm2timestamp((struct pg_tm*) &pgss->bucket_start_time[entry->key.bucket_id], 0, NULL, &tm);
|
||||
values[i++] = TimestampGetDatum(tm);
|
||||
}
|
||||
if (tmp.calls.calls == 0)
|
||||
{
|
||||
/* Query of pg_stat_monitor itslef started from zero count */
|
||||
|
@ -1937,6 +2010,12 @@ pg_stat_monitor_internal(FunctionCallInfo fcinfo,
|
|||
values[i++] = Float8GetDatumFast(tmp.blocks.blk_read_time);
|
||||
values[i++] = Float8GetDatumFast(tmp.blocks.blk_write_time);
|
||||
|
||||
if (api_version >= PGSM_V2_0)
|
||||
{
|
||||
values[i++] = Float8GetDatumFast(tmp.blocks.temp_blk_read_time);
|
||||
values[i++] = Float8GetDatumFast(tmp.blocks.temp_blk_write_time);
|
||||
}
|
||||
|
||||
/* resp_calls at column number 41 */
|
||||
values[i++] = IntArrayGetTextDatum(tmp.resp_calls, PGSM_HISTOGRAM_BUCKETS);
|
||||
|
||||
|
@ -1970,8 +2049,24 @@ pg_stat_monitor_internal(FunctionCallInfo fcinfo,
|
|||
values[i++] = CStringGetTextDatum(tmp.info.comments);
|
||||
else
|
||||
nulls[i++] = true;
|
||||
|
||||
if (api_version >= PGSM_V2_0)
|
||||
{
|
||||
values[i++] = Int64GetDatumFast(tmp.jitinfo.jit_functions);
|
||||
values[i++] = Float8GetDatumFast(tmp.jitinfo.jit_generation_time);
|
||||
values[i++] = Int64GetDatumFast(tmp.jitinfo.jit_inlining_count);
|
||||
values[i++] = Float8GetDatumFast(tmp.jitinfo.jit_inlining_time);
|
||||
values[i++] = Int64GetDatumFast(tmp.jitinfo.jit_optimization_count);
|
||||
values[i++] = Float8GetDatumFast(tmp.jitinfo.jit_optimization_time);
|
||||
values[i++] = Int64GetDatumFast(tmp.jitinfo.jit_emission_count);
|
||||
values[i++] = Float8GetDatumFast(tmp.jitinfo.jit_emission_time);
|
||||
}
|
||||
|
||||
}
|
||||
values[i++] = BoolGetDatum(toplevel);
|
||||
values[i++] = BoolGetDatum(pg_atomic_read_u64(&pgss->current_wbucket) != bucketid);
|
||||
|
||||
/* clean up and return the tuplestore */
|
||||
tuplestore_putvalues(tupstore, tupdesc, values, nulls);
|
||||
}
|
||||
/* clean up and return the tuplestore */
|
||||
|
@ -2055,13 +2150,16 @@ get_next_wbucket(pgssSharedState *pgss)
|
|||
|
||||
tv.tv_sec = (tv.tv_sec) - (tv.tv_sec % PGSM_BUCKET_TIME);
|
||||
lt = localtime(&tv.tv_sec);
|
||||
/*
|
||||
* Year is 1900 behind and month is 0 based, therefore we need to
|
||||
* adjust that.
|
||||
*/
|
||||
lt->tm_year += 1900;
|
||||
lt->tm_mon += 1;
|
||||
|
||||
/* Allign the value in prev_bucket_sec to the bucket start time */
|
||||
pg_atomic_exchange_u64(&pgss->prev_bucket_sec, (uint64)tv.tv_sec);
|
||||
|
||||
snprintf(pgss->bucket_start_time[new_bucket_id], sizeof(pgss->bucket_start_time[new_bucket_id]),
|
||||
"%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);
|
||||
|
||||
memcpy(&pgss->bucket_start_time[new_bucket_id], lt, sizeof(struct tm));
|
||||
return new_bucket_id;
|
||||
}
|
||||
|
||||
|
@ -3205,136 +3303,6 @@ SaveQueryText(uint64 bucketid,
|
|||
return true;
|
||||
}
|
||||
|
||||
Datum
|
||||
pg_stat_monitor_settings(PG_FUNCTION_ARGS)
|
||||
{
|
||||
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
|
||||
TupleDesc tupdesc;
|
||||
Tuplestorestate *tupstore;
|
||||
MemoryContext per_query_ctx;
|
||||
MemoryContext oldcontext;
|
||||
int i;
|
||||
|
||||
/* 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_settings: return type must be a row type");
|
||||
return (Datum) 0;
|
||||
}
|
||||
|
||||
if (tupdesc->natts != 8)
|
||||
{
|
||||
elog(ERROR, "pg_stat_monitor_settings: incorrect number of output arguments, required: 7, found %d", tupdesc->natts);
|
||||
return (Datum) 0;
|
||||
}
|
||||
|
||||
tupstore = tuplestore_begin_heap(true, false, work_mem);
|
||||
rsinfo->returnMode = SFRM_Materialize;
|
||||
rsinfo->setResult = tupstore;
|
||||
rsinfo->setDesc = tupdesc;
|
||||
|
||||
MemoryContextSwitchTo(oldcontext);
|
||||
|
||||
for (i = 0; i < MAX_SETTINGS; i++)
|
||||
{
|
||||
Datum values[8];
|
||||
bool nulls[8];
|
||||
int j = 0;
|
||||
char options[1024] = "";
|
||||
GucVariable *conf;
|
||||
|
||||
memset(values, 0, sizeof(values));
|
||||
memset(nulls, 0, sizeof(nulls));
|
||||
|
||||
conf = get_conf(i);
|
||||
|
||||
values[j++] = CStringGetTextDatum(conf->guc_name);
|
||||
|
||||
/* Handle current and default values. */
|
||||
switch (conf->type)
|
||||
{
|
||||
case PGC_ENUM:
|
||||
values[j++] = CStringGetTextDatum(conf->guc_options[conf->guc_variable]);
|
||||
values[j++] = CStringGetTextDatum(conf->guc_options[conf->guc_default]);
|
||||
break;
|
||||
|
||||
case PGC_INT:
|
||||
{
|
||||
char value[32];
|
||||
|
||||
sprintf(value, "%d", conf->guc_variable);
|
||||
values[j++] = CStringGetTextDatum(value);
|
||||
|
||||
sprintf(value, "%d", conf->guc_default);
|
||||
values[j++] = CStringGetTextDatum(value);
|
||||
break;
|
||||
}
|
||||
|
||||
case PGC_BOOL:
|
||||
values[j++] = CStringGetTextDatum(conf->guc_variable ? "yes" : "no");
|
||||
values[j++] = CStringGetTextDatum(conf->guc_default ? "yes" : "no");
|
||||
break;
|
||||
|
||||
default:
|
||||
Assert(false);
|
||||
}
|
||||
|
||||
values[j++] = CStringGetTextDatum(get_conf(i)->guc_desc);
|
||||
|
||||
/* Minimum and maximum displayed only for integers or real numbers. */
|
||||
if (conf->type != PGC_INT)
|
||||
{
|
||||
nulls[j++] = true;
|
||||
nulls[j++] = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
values[j++] = Int64GetDatumFast(get_conf(i)->guc_min);
|
||||
values[j++] = Int64GetDatumFast(get_conf(i)->guc_max);
|
||||
}
|
||||
|
||||
if (conf->type == PGC_ENUM)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
strcat(options, conf->guc_options[0]);
|
||||
for (i = 1; i < conf->n_options; ++i)
|
||||
{
|
||||
strcat(options, ", ");
|
||||
strcat(options, conf->guc_options[i]);
|
||||
}
|
||||
}
|
||||
else if (conf->type == PGC_BOOL)
|
||||
{
|
||||
strcat(options, "yes, no");
|
||||
}
|
||||
|
||||
values[j++] = CStringGetTextDatum(options);
|
||||
values[j++] = CStringGetTextDatum(get_conf(i)->guc_restart ? "yes" : "no");
|
||||
tuplestore_putvalues(tupstore, tupdesc, values, nulls);
|
||||
}
|
||||
/* clean up and return the tuplestore */
|
||||
tuplestore_donestoring(tupstore);
|
||||
return (Datum) 0;
|
||||
}
|
||||
|
||||
|
||||
Datum
|
||||
pg_stat_monitor_hook_stats(PG_FUNCTION_ARGS)
|
||||
{
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include "catalog/pg_authid.h"
|
||||
#include "executor/instrument.h"
|
||||
#include "common/ip.h"
|
||||
#include "jit/jit.h"
|
||||
#include "funcapi.h"
|
||||
#include "access/twophase.h"
|
||||
#include "mb/pg_wchar.h"
|
||||
|
@ -255,8 +256,27 @@ typedef struct Blocks
|
|||
int64 temp_blks_written; /* # of temp blocks written */
|
||||
double blk_read_time; /* time spent reading, in msec */
|
||||
double blk_write_time; /* time spent writing, in msec */
|
||||
|
||||
double temp_blk_read_time; /* time spent reading temp blocks, in msec */
|
||||
double temp_blk_write_time; /* time spent writing temp blocks, in
|
||||
* msec */
|
||||
} Blocks;
|
||||
|
||||
typedef struct JitInfo
|
||||
{
|
||||
int64 jit_functions; /* total number of JIT functions emitted */
|
||||
double jit_generation_time; /* total time to generate jit code */
|
||||
int64 jit_inlining_count; /* number of times inlining time has been
|
||||
* > 0 */
|
||||
double jit_inlining_time; /* total time to inline jit code */
|
||||
int64 jit_optimization_count; /* number of times optimization time
|
||||
* has been > 0 */
|
||||
double jit_optimization_time; /* total time to optimize jit code */
|
||||
int64 jit_emission_count; /* number of times emission time has been
|
||||
* > 0 */
|
||||
double jit_emission_time; /* total time to emit jit code */
|
||||
} JitInfo;
|
||||
|
||||
typedef struct SysInfo
|
||||
{
|
||||
float utime; /* user cpu time */
|
||||
|
@ -283,11 +303,12 @@ typedef struct Counters
|
|||
|
||||
Blocks blocks;
|
||||
SysInfo sysinfo;
|
||||
JitInfo jitinfo;
|
||||
ErrorInfo error;
|
||||
Wal_Usage walusage;
|
||||
int resp_calls[MAX_RESPONSE_BUCKET]; /* execution time's in
|
||||
* msec */
|
||||
uint64 state; /* query state */
|
||||
int64 state; /* query state */
|
||||
} Counters;
|
||||
|
||||
/* Some global structure to get the cpu usage, really don't like the idea of global variable */
|
||||
|
@ -317,11 +338,9 @@ typedef struct pgssSharedState
|
|||
pg_atomic_uint64 current_wbucket;
|
||||
pg_atomic_uint64 prev_bucket_sec;
|
||||
uint64 bucket_entry[MAX_BUCKETS];
|
||||
char bucket_start_time[MAX_BUCKETS][60]; /* start time of the
|
||||
* bucket */
|
||||
struct tm bucket_start_time[MAX_BUCKETS]; /* start time of the bucket */
|
||||
LWLock *errors_lock; /* protects errors hashtable
|
||||
* search/modification */
|
||||
|
||||
/*
|
||||
* These variables are used when pgsm_overflow_target is ON.
|
||||
*
|
||||
|
|
|
@ -27,11 +27,15 @@ SELECT query, elevel, sqlcode, message FROM pg_stat_monitor ORDER BY query COLLA
|
|||
SELECT * FROM unknown; | 21 | 42P01 | relation "unknown" does not exist
|
||||
SELECT 1/0; | 21 | 22012 | division by zero
|
||||
SELECT pg_stat_monitor_reset() | 0 | |
|
||||
do $$ +| 0 | |
|
||||
BEGIN +| | |
|
||||
RAISE WARNING 'warning message';+| | |
|
||||
END $$ | | |
|
||||
do $$ +| 19 | 01000 | warning message
|
||||
BEGIN +| | |
|
||||
RAISE WARNING 'warning message';+| | |
|
||||
END $$; | | |
|
||||
(5 rows)
|
||||
(6 rows)
|
||||
|
||||
SELECT pg_stat_monitor_reset();
|
||||
pg_stat_monitor_reset
|
||||
|
|
|
@ -19,10 +19,11 @@ Drop Table if exists Company;
|
|||
SELECT query, elevel, sqlcode, message FROM pg_stat_monitor ORDER BY query COLLATE "C",elevel;
|
||||
query | elevel | sqlcode | message
|
||||
-------------------------------------------------------+--------+---------+---------------------------------------------------------------
|
||||
Drop Table if exists Company | 0 | |
|
||||
INSERT INTO Company(ID, Name) VALUES (1, 'Percona') | 0 | |
|
||||
INSERT INTO Company(ID, Name) VALUES (1, 'Percona'); | 21 | 23505 | duplicate key value violates unique constraint "company_pkey"
|
||||
SELECT pg_stat_monitor_reset() | 0 | |
|
||||
(3 rows)
|
||||
(4 rows)
|
||||
|
||||
SELECT pg_stat_monitor_reset();
|
||||
pg_stat_monitor_reset
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
CREATE USER su WITH SUPERUSER;
|
||||
SET ROLE su;
|
||||
CREATE EXTENSION pg_stat_monitor;
|
||||
CREATE USER u1;
|
||||
SELECT pg_stat_monitor_reset();
|
||||
pg_stat_monitor_reset
|
||||
-----------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
SELECT routine_schema, routine_name, routine_type, data_type FROM information_schema.routines WHERE routine_schema = 'public' ORDER BY routine_name COLLATE "C";
|
||||
routine_schema | routine_name | routine_type | data_type
|
||||
----------------+--------------------------+--------------+-----------
|
||||
public | decode_error_level | FUNCTION | text
|
||||
public | get_cmd_type | FUNCTION | text
|
||||
public | get_histogram_timings | FUNCTION | text
|
||||
public | histogram | FUNCTION | record
|
||||
public | pg_stat_monitor_internal | FUNCTION | record
|
||||
public | pg_stat_monitor_reset | FUNCTION | void
|
||||
public | pg_stat_monitor_version | FUNCTION | text
|
||||
public | pgsm_create_11_view | FUNCTION | integer
|
||||
public | pgsm_create_13_view | FUNCTION | integer
|
||||
public | pgsm_create_14_view | FUNCTION | integer
|
||||
public | pgsm_create_15_view | FUNCTION | integer
|
||||
public | pgsm_create_view | FUNCTION | integer
|
||||
public | range | FUNCTION | ARRAY
|
||||
(13 rows)
|
||||
|
||||
SET ROLE u1;
|
||||
SELECT routine_schema, routine_name, routine_type, data_type FROM information_schema.routines WHERE routine_schema = 'public' ORDER BY routine_name COLLATE "C";
|
||||
routine_schema | routine_name | routine_type | data_type
|
||||
----------------+-------------------------+--------------+-----------
|
||||
public | histogram | FUNCTION | record
|
||||
public | pg_stat_monitor_reset | FUNCTION | void
|
||||
public | pg_stat_monitor_version | FUNCTION | text
|
||||
(3 rows)
|
||||
|
||||
set role su;
|
||||
DROP USER u1;
|
||||
DROP EXTENSION pg_stat_monitor;
|
|
@ -36,4 +36,4 @@ SELECT pg_stat_monitor_reset();
|
|||
|
||||
(1 row)
|
||||
|
||||
DROP EXTENSION pg_stat_monitor;
|
||||
DROP EXTENSION pg_stat_monitor;
|
|
@ -24,12 +24,23 @@ SELECT add2(1,2);
|
|||
(1 row)
|
||||
|
||||
SELECT query, top_query FROM pg_stat_monitor ORDER BY query COLLATE "C";
|
||||
query | top_query
|
||||
--------------------------------+------------------
|
||||
(select $1 + $2) | SELECT add2(1,2)
|
||||
SELECT add2(1,2) |
|
||||
SELECT pg_stat_monitor_reset() |
|
||||
(3 rows)
|
||||
query | top_query
|
||||
-------------------------------------------------------------+------------------
|
||||
(select $1 + $2) | SELECT add2(1,2)
|
||||
CREATE OR REPLACE FUNCTION add(int, int) RETURNS INTEGER AS+|
|
||||
$$ +|
|
||||
BEGIN +|
|
||||
return (select $1 + $2); +|
|
||||
END; $$ language plpgsql |
|
||||
CREATE OR REPLACE function add2(int, int) RETURNS int as +|
|
||||
$$ +|
|
||||
BEGIN +|
|
||||
return add($1,$2); +|
|
||||
END; +|
|
||||
$$ language plpgsql |
|
||||
SELECT add2(1,2) |
|
||||
SELECT pg_stat_monitor_reset() |
|
||||
(5 rows)
|
||||
|
||||
SELECT pg_stat_monitor_reset();
|
||||
pg_stat_monitor_reset
|
||||
|
|
|
@ -2,7 +2,7 @@ CREATE EXTENSION pg_stat_monitor;
|
|||
SELECT pg_stat_monitor_version();
|
||||
pg_stat_monitor_version
|
||||
-------------------------
|
||||
1.1.1
|
||||
2.0.0-dev
|
||||
(1 row)
|
||||
|
||||
DROP EXTENSION pg_stat_monitor;
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
CREATE USER su WITH SUPERUSER;
|
||||
SET ROLE su;
|
||||
|
||||
CREATE EXTENSION pg_stat_monitor;
|
||||
CREATE USER u1;
|
||||
|
||||
SELECT pg_stat_monitor_reset();
|
||||
SELECT routine_schema, routine_name, routine_type, data_type FROM information_schema.routines WHERE routine_schema = 'public' ORDER BY routine_name COLLATE "C";
|
||||
|
||||
SET ROLE u1;
|
||||
SELECT routine_schema, routine_name, routine_type, data_type FROM information_schema.routines WHERE routine_schema = 'public' ORDER BY routine_name COLLATE "C";
|
||||
|
||||
set role su;
|
||||
DROP USER u1;
|
||||
DROP EXTENSION pg_stat_monitor;
|
|
@ -1,6 +1,31 @@
|
|||
CREATE EXTENSION pg_stat_monitor;
|
||||
SELECT pg_stat_monitor_reset();
|
||||
select pg_sleep(.5);
|
||||
SELECT * FROM pg_stat_monitor_settings WHERE name NOT LIKE 'pg_stat_monitor.pgsm_track_planning' ORDER BY name COLLATE "C";
|
||||
SELECT pg_stat_monitor_reset();
|
||||
|
||||
\x
|
||||
|
||||
SELECT name
|
||||
, setting
|
||||
, unit
|
||||
, category
|
||||
, short_desc
|
||||
, extra_desc
|
||||
, context
|
||||
, vartype
|
||||
, source
|
||||
, min_val
|
||||
, max_val
|
||||
, enumvals
|
||||
, boot_val
|
||||
, reset_val
|
||||
, sourcefile
|
||||
, sourceline
|
||||
, pending_restart
|
||||
FROM pg_settings
|
||||
WHERE name LIKE 'pg_stat_monitor.%'
|
||||
AND name NOT LIKE 'pg_stat_monitor.pgsm_track_planning'
|
||||
ORDER
|
||||
BY name
|
||||
COLLATE "C";
|
||||
|
||||
\x
|
||||
|
||||
DROP EXTENSION pg_stat_monitor;
|
||||
|
|
|
@ -0,0 +1,104 @@
|
|||
#!/usr/bin/perl
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use File::Basename;
|
||||
use File::Compare;
|
||||
use File::Copy;
|
||||
use String::Util qw(trim);
|
||||
use Test::More;
|
||||
use lib 't';
|
||||
use pgsm;
|
||||
|
||||
# Get filename and create out file name and dirs where requried
|
||||
PGSM::setup_files_dir(basename($0));
|
||||
|
||||
# Create new PostgreSQL node and do initdb
|
||||
my $node = PGSM->pgsm_init_pg();
|
||||
my $pgdata = $node->data_dir;
|
||||
|
||||
# Update postgresql.conf to include/load pg_stat_monitor library
|
||||
open my $conf, '>>', "$pgdata/postgresql.conf";
|
||||
print $conf "shared_preload_libraries = 'pg_stat_monitor'\n";
|
||||
close $conf;
|
||||
|
||||
# Dictionary for expected PGSM columns names on different PG server versions
|
||||
my %pg_versions_pgsm_columns = ( 15 => "application_name,blk_read_time," .
|
||||
"blk_write_time,bucket,bucket_done,bucket_start_time,calls," .
|
||||
"client_ip,cmd_type,cmd_type_text,comments,cpu_sys_time,cpu_user_time," .
|
||||
"datname,elevel,jit_emission_count,jit_emission_time,jit_functions," .
|
||||
"jit_generation_time,jit_inlining_count,jit_inlining_time," .
|
||||
"jit_optimization_count,jit_optimization_time," .
|
||||
"local_blks_dirtied,local_blks_hit,local_blks_read," .
|
||||
"local_blks_written,max_exec_time,max_plan_time,mean_exec_time," .
|
||||
"mean_plan_time,message,min_exec_time,min_plan_time,planid," .
|
||||
"plans_calls,query,query_plan,queryid,relations,resp_calls," .
|
||||
"rows_retrieved,shared_blks_dirtied,shared_blks_hit,shared_blks_read," .
|
||||
"shared_blks_written,sqlcode,stddev_exec_time,stddev_plan_time," .
|
||||
"temp_blk_read_time,temp_blk_write_time,temp_blks_read,temp_blks_written," .
|
||||
"top_query,top_queryid,toplevel,total_exec_time,total_plan_time," .
|
||||
"userid,wal_bytes,wal_fpi,wal_records",
|
||||
14 => "application_name,blk_read_time," .
|
||||
"blk_write_time,bucket,bucket_done,bucket_start_time,calls," .
|
||||
"client_ip,cmd_type,cmd_type_text,comments,cpu_sys_time,cpu_user_time," .
|
||||
"datname,elevel,local_blks_dirtied,local_blks_hit,local_blks_read," .
|
||||
"local_blks_written,max_exec_time,max_plan_time,mean_exec_time," .
|
||||
"mean_plan_time,message,min_exec_time,min_plan_time,planid," .
|
||||
"plans_calls,query,query_plan,queryid,relations,resp_calls," .
|
||||
"rows_retrieved,shared_blks_dirtied,shared_blks_hit,shared_blks_read," .
|
||||
"shared_blks_written,sqlcode,stddev_exec_time,stddev_plan_time," .
|
||||
"temp_blks_read,temp_blks_written,top_query,top_queryid,toplevel," .
|
||||
"total_exec_time,total_plan_time,userid,wal_bytes,wal_fpi,wal_records",
|
||||
13 => "application_name,blk_read_time," .
|
||||
"blk_write_time,bucket,bucket_done,bucket_start_time,calls," .
|
||||
"client_ip,cmd_type,cmd_type_text,comments,cpu_sys_time,cpu_user_time," .
|
||||
"datname,elevel,local_blks_dirtied,local_blks_hit,local_blks_read," .
|
||||
"local_blks_written,max_exec_time,max_plan_time,mean_exec_time," .
|
||||
"mean_plan_time,message,min_exec_time,min_plan_time,planid," .
|
||||
"plans_calls,query,query_plan,queryid,relations,resp_calls," .
|
||||
"rows_retrieved,shared_blks_dirtied,shared_blks_hit,shared_blks_read," .
|
||||
"shared_blks_written,sqlcode,stddev_exec_time,stddev_plan_time," .
|
||||
"temp_blks_read,temp_blks_written,top_query,top_queryid,toplevel," .
|
||||
"total_exec_time,total_plan_time,userid,wal_bytes,wal_fpi,wal_records",
|
||||
12 => "application_name,blk_read_time,blk_write_time,bucket,bucket_done," .
|
||||
"bucket_start_time,calls,client_ip,cmd_type,cmd_type_text,comments," .
|
||||
"cpu_sys_time,cpu_user_time,datname,elevel,local_blks_dirtied," .
|
||||
"local_blks_hit,local_blks_read,local_blks_written,max_time,mean_time," .
|
||||
"message,min_time,planid,query,query_plan,queryid,relations,resp_calls," .
|
||||
"rows_retrieved,shared_blks_dirtied,shared_blks_hit,shared_blks_read," .
|
||||
"shared_blks_written,sqlcode,stddev_time,temp_blks_read,temp_blks_written," .
|
||||
"top_query,top_queryid,total_time,userid"
|
||||
);
|
||||
|
||||
# Start server
|
||||
my $rt_value = $node->start;
|
||||
ok($rt_value == 1, "Start Server");
|
||||
|
||||
# Create extension and change out file permissions
|
||||
my ($cmdret, $stdout, $stderr) = $node->psql('postgres', 'CREATE EXTENSION pg_stat_monitor;', extra_params => ['-a']);
|
||||
ok($cmdret == 0, "Create PGSM Extension");
|
||||
PGSM::append_to_file($stdout . "\n");
|
||||
|
||||
# Get PGSM columns names from PGSM installation in server
|
||||
($cmdret, $stdout, $stderr) = $node->psql('postgres', "SELECT column_name FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = 'pg_stat_monitor' order by column_name;", extra_params => ['-A', '-R,', '-Ptuples_only=on']);
|
||||
ok($cmdret == 0, "Get columns names in PGSM installation for PG version $PGSM::PG_MAJOR_VERSION");
|
||||
PGSM::append_to_file($stdout . "\n");
|
||||
|
||||
# Compare PGSM column names in installation to expected column names
|
||||
ok($stdout eq $pg_versions_pgsm_columns{$PGSM::PG_MAJOR_VERSION}, "Compare supported columns names for PG version $PGSM::PG_MAJOR_VERSION against expected");
|
||||
|
||||
# Run Select statement against expected column names
|
||||
($cmdret, $stdout, $stderr) = $node->psql('postgres', "Select $pg_versions_pgsm_columns{$PGSM::PG_MAJOR_VERSION} from pg_stat_monitor;", extra_params => ['-a', '-Pformat=aligned','-Ptuples_only=off']);
|
||||
ok($cmdret == 0, "Select statement against expected column names");
|
||||
PGSM::append_to_file($stdout);
|
||||
|
||||
# Drop extension
|
||||
$stdout = $node->safe_psql('postgres', 'Drop extension pg_stat_monitor;', extra_params => ['-a']);
|
||||
ok($cmdret == 0, "Drop PGSM Extension");
|
||||
PGSM::append_to_file($stdout);
|
||||
|
||||
# Stop the server
|
||||
$node->stop;
|
||||
|
||||
# Done testing for this testcase file.
|
||||
done_testing();
|
Loading…
Reference in New Issue