diff --git a/.github/workflows/postgresql-17-build.yml b/.github/workflows/postgresql-17-build.yml new file mode 100644 index 00000000..a7b7f6e1 --- /dev/null +++ b/.github/workflows/postgresql-17-build.yml @@ -0,0 +1,147 @@ +name: postgresql-17-build +on: + pull_request: + push: + branches: + - main + tags: + - '[0-9]+.[0-9]+.[0-9]+*' + +jobs: + build: + name: pg-17-build-test + runs-on: ubuntu-22.04 + steps: + - name: Clone postgres repository + uses: actions/checkout@v4 + with: + repository: 'postgres/postgres' + ref: 'REL_17_BETA2' # TODO: Replace with REL_17_STABLE + + - name: Install dependencies + run: | + sudo apt-get update + sudo apt purge postgresql-client-common postgresql-common \ + postgresql postgresql* + sudo apt-get install -y libreadline6-dev systemtap-sdt-dev \ + zlib1g-dev libssl-dev libpam0g-dev bison flex \ + libipc-run-perl -y docbook-xsl docbook-xsl libxml2 libxml2-utils \ + libxml2-dev libxslt-dev xsltproc libkrb5-dev libldap2-dev \ + libsystemd-dev gettext tcl-dev libperl-dev pkg-config clang-11 \ + llvm-11 llvm-11-dev libselinux1-dev python3-dev \ + uuid-dev liblz4-dev + sudo rm -rf /var/lib/postgresql /var/log/postgresql /etc/postgresql \ + /usr/lib/postgresql /usr/include/postgresql /usr/share/postgresql \ + /etc/postgresql + sudo rm -f /usr/bin/pg_config + sudo /usr/bin/perl -MCPAN -e 'install IPC::Run' + sudo /usr/bin/perl -MCPAN -e 'install Text::Trim' + + - name: Create pgsql dir + run: mkdir -p /opt/pgsql + + - name: Build postgres + run: | + export PATH="/opt/pgsql/bin:$PATH" + ./configure '--build=x86_64-linux-gnu' '--prefix=/usr' \ + '--includedir=${prefix}/include' '--mandir=${prefix}/share/man' \ + '--infodir=${prefix}/share/info' '--sysconfdir=/etc' \ + '--localstatedir=/var' '--libdir=${prefix}/lib/x86_64-linux-gnu' \ + '--libexecdir=${prefix}/lib/x86_64-linux-gnu' '--with-icu' \ + '--with-tcl' '--with-perl' '--with-python' '--with-pam' \ + '--with-openssl' '--with-libxml' '--with-libxslt' '--with-ldap' \ + 'PYTHON=/usr/bin/python3' '--mandir=/usr/share/postgresql/17/man' \ + '--docdir=/usr/share/doc/postgresql-doc-17' '--with-pgport=5432' \ + '--sysconfdir=/etc/postgresql-common' '--datarootdir=/usr/share' \ + '--datadir=/usr/share/postgresql/17' '--with-uuid=e2fs' \ + '--bindir=/usr/lib/postgresql/17/bin' '--enable-tap-tests' \ + '--libdir=/usr/lib/x86_64-linux-gnu' '--enable-debug' \ + '--libexecdir=/usr/lib/postgresql' '--with-gnu-ld' \ + '--includedir=/usr/include/postgresql' '--enable-dtrace' \ + '--enable-nls' '--enable-thread-safety' '--disable-rpath' \ + '--with-system-tzdata=/usr/share/zoneinfo' '--with-llvm' \ + 'LLVM_CONFIG=/usr/bin/llvm-config-11' 'CLANG=/usr/bin/clang-11' \ + '--with-systemd' '--with-selinux' 'MKDIR_P=/bin/mkdir -p' \ + 'PROVE=/usr/bin/prove' 'TAR=/bin/tar' 'XSLTPROC=xsltproc --nonet' \ + 'LDFLAGS=-Wl,-Bsymbolic-functions -Wl,-z,relro -Wl,-z,now' \ + 'build_alias=x86_64-linux-gnu' '--with-gssapi' \ + 'CPPFLAGS=-Wdate-time -D_FORTIFY_SOURCE=2' \ + 'CFLAGS=-g -O2 -fstack-protector-strong -Wformat -Werror=format-security -fno-omit-frame-pointer' \ + 'CXXFLAGS=-g -O2 -fstack-protector-strong -Wformat -Werror=format-security' + make world + sudo make install-world + + - name: Start postgresql cluster + run: | + export PATH="/usr/lib/postgresql/17/bin:$PATH" + sudo cp /usr/lib/postgresql/17/bin/pg_config /usr/bin + initdb -D /opt/pgsql/data + pg_ctl -D /opt/pgsql/data -l logfile start + + - name: Clone pg_stat_monitor repository + uses: actions/checkout@v4 + with: + path: 'src/pg_stat_monitor' + + - name: Build pg_stat_monitor + run: | + make USE_PGXS=1 + sudo make USE_PGXS=1 install + working-directory: src/pg_stat_monitor + + - name: Configure and Restart Server + run: | + export PATH="/usr/lib/postgresql/17/bin:$PATH" + pg_ctl -D /opt/pgsql/data -l logfile stop + echo "shared_preload_libraries = 'pg_stat_monitor'" >> \ + /opt/pgsql/data/postgresql.conf + echo "compute_query_id = regress" >> /opt/pgsql/data/postgresql.conf + pg_ctl -D /opt/pgsql/data -l logfile start + working-directory: src/pg_stat_monitor + + - name: Start pg_stat_monitor_tests + run: | + make installcheck + working-directory: src/pg_stat_monitor/ + + - name: Change dir permissions on fail + if: ${{ failure() }} + run: | + sudo chmod -R ugo+rwx t + sudo chmod -R ugo+rwx tmp_check + exit 2 # regenerate error so that we can upload files in next step + working-directory: src/pg_stat_monitor + + - name: Upload logs on fail + if: ${{ failure() }} + uses: actions/upload-artifact@v4 + with: + name: Regressions diff and postgresql log + path: | + src/pg_stat_monitor/regression.diffs + src/pg_stat_monitor/regression.out + src/pg_stat_monitor/logfile + src/pg_stat_monitor/t/results/ + src/pg_stat_monitor/tmp_check/log/ + !src/pg_stat_monitor/tmp_check/**/archives/* + !src/pg_stat_monitor/tmp_check/**/backup/* + !src/pg_stat_monitor/tmp_check/**/pgdata/* + !src/pg_stat_monitor/tmp_check/**/archives/ + !src/pg_stat_monitor/tmp_check/**/backup/ + !src/pg_stat_monitor/tmp_check/**/pgdata/ + if-no-files-found: warn + retention-days: 3 + + - name: Start Server installcheck-world tests + run: make installcheck-world + + - name: Report on installcheck-world test suites fail + uses: actions/upload-artifact@v4 + if: ${{ failure() }} + with: + name: Regressions output files of failed testsuite, and pg log + path: | + **/regression.diffs + **/regression.out + src/pg_stat_monitor/logfile + retention-days: 3 diff --git a/.github/workflows/postgresql-17-pgdg-package.yml b/.github/workflows/postgresql-17-pgdg-package.yml new file mode 100644 index 00000000..2ba82b1a --- /dev/null +++ b/.github/workflows/postgresql-17-pgdg-package.yml @@ -0,0 +1,93 @@ +name: postgresql-17-pgdg-package +on: + pull_request: + push: + branches: + - main + tags: + - '[0-9]+.[0-9]+.[0-9]+*' + +jobs: + build: + name: pg-17-pgdg-package-test + runs-on: ubuntu-22.04 + steps: + - name: Clone pg_stat_monitor repository + uses: actions/checkout@v4 + with: + path: 'src/pg_stat_monitor' + + - name: Delete old postgresql files + run: | + sudo apt-get update + sudo apt purge postgresql-client-common postgresql-common \ + postgresql postgresql* + sudo apt-get install -y libreadline6-dev systemtap-sdt-dev wget \ + zlib1g-dev libssl-dev libpam0g-dev bison flex libipc-run-perl + sudo rm -rf /var/lib/postgresql /var/log/postgresql /etc/postgresql \ + /usr/lib/postgresql /usr/include/postgresql /usr/share/postgresql \ + /etc/postgresql + sudo rm -f /usr/bin/pg_config + sudo /usr/bin/perl -MCPAN -e 'install IPC::Run' + sudo /usr/bin/perl -MCPAN -e 'install Text::Trim' + + - name: Install PG Distribution Postgresql 17 + run: | + sudo sh -c 'echo "deb http://apt.postgresql.org/pub/repos/apt \ + $(lsb_release -cs)-pgdg main 17" > /etc/apt/sources.list.d/pgdg.list' + sudo wget --quiet -O - \ + https://www.postgresql.org/media/keys/ACCC4CF8.asc | + sudo apt-key add - + sudo apt update + sudo apt -y install postgresql-17 postgresql-server-dev-17 + + - name: Change src owner to postgres + run: | + sudo chmod o+rx ~ + sudo chown -R postgres:postgres src + + - name: Build pg_stat_monitor + run: | + sudo -u postgres bash -c 'make USE_PGXS=1' + sudo make USE_PGXS=1 install + working-directory: src/pg_stat_monitor + + - name: Start pg_stat_monitor_tests + run: | + sudo service postgresql stop + echo "shared_preload_libraries = 'pg_stat_monitor'" | + sudo tee -a /etc/postgresql/17/main/postgresql.conf + sudo service postgresql start + sudo psql -V + export PG_TEST_PORT_DIR=${GITHUB_WORKSPACE}/src/pg_stat_monitor + echo $PG_TEST_PORT_DIR + sudo -E -u postgres bash -c 'make installcheck USE_PGXS=1' + working-directory: src/pg_stat_monitor + + - name: Change dir permissions on fail + if: ${{ failure() }} + run: | + sudo chmod -R ugo+rwx t + sudo chmod -R ugo+rwx tmp_check + exit 2 # regenerate error so that we can upload files in next step + working-directory: src/pg_stat_monitor + + - name: Upload logs on fail + if: ${{ failure() }} + uses: actions/upload-artifact@v4 + with: + name: Regressions diff and postgresql log + path: | + src/pg_stat_monitor/regression.diffs + src/pg_stat_monitor/regression.out + src/pg_stat_monitor/logfile + src/pg_stat_monitor/t/results/ + src/pg_stat_monitor/tmp_check/log/ + !src/pg_stat_monitor/tmp_check/**/archives/* + !src/pg_stat_monitor/tmp_check/**/backup/* + !src/pg_stat_monitor/tmp_check/**/pgdata/* + !src/pg_stat_monitor/tmp_check/**/archives/ + !src/pg_stat_monitor/tmp_check/**/backup/ + !src/pg_stat_monitor/tmp_check/**/pgdata/ + if-no-files-found: warn + retention-days: 3 diff --git a/Makefile b/Makefile index 51fb11d5..117c4871 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--2.0.sql pg_stat_monitor--1.0--2.0.sql +DATA = pg_stat_monitor--2.0.sql pg_stat_monitor--1.0--2.0.sql pg_stat_monitor--2.0--2.1.sql PGFILEDESC = "pg_stat_monitor - execution statistics of SQL statements" @@ -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 pgsm_query_id functions counters relations database error_insert application_name application_name_unique top_query cmd_type error rows tags user +REGRESS = basic version guc pgsm_query_id functions counters relations database error_insert application_name application_name_unique top_query cmd_type error rows tags user level_tracking # Disabled because these tests require "shared_preload_libraries=pg_stat_statements", # which typical installcheck users do not have (e.g. buildfarm clients). diff --git a/README.md b/README.md index 38259222..907e4460 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ [![postgresql-14-pgdg-package](https://github.com/percona/pg_stat_monitor/actions/workflows/postgresql-14-pgdg-package.yml/badge.svg)](https://github.com/percona/pg_stat_monitor/actions/workflows/postgresql-14-pgdg-package.yml) [![postgresql-15-pgdg-package](https://github.com/percona/pg_stat_monitor/actions/workflows/postgresql-15-pgdg-package.yml/badge.svg)](https://github.com/percona/pg_stat_monitor/actions/workflows/postgresql-15-pgdg-package.yml) [![postgresql-16-pgdg-package](https://github.com/percona/pg_stat_monitor/actions/workflows/postgresql-16-pgdg-package.yml/badge.svg)](https://github.com/percona/pg_stat_monitor/actions/workflows/postgresql-16-pgdg-package.yml) +[![postgresql-17-pgdg-package](https://github.com/percona/pg_stat_monitor/actions/workflows/postgresql-17-pgdg-package.yml/badge.svg)](https://github.com/percona/pg_stat_monitor/actions/workflows/postgresql-17-pgdg-package.yml) [![PGXN version](https://badge.fury.io/pg/pg_stat_monitor.svg)](https://badge.fury.io/pg/pg_stat_monitor) [![Code coverage](https://codecov.io/gh/percona/pg_stat_monitor/branch/main/graph/badge.svg)](https://codecov.io/gh/percona/pg_stat_monitor) @@ -51,7 +52,7 @@ To learn about other features, available in `pg_stat_monitor`, see the [Features `pg_stat_monitor` supports PostgreSQL versions 11 and above. It is compatible with both PostgreSQL provided by PostgreSQL Global Development Group (PGDG) and [Percona Distribution for PostgreSQL](https://www.percona.com/software/postgresql-distribution). -The `RPM` (for RHEL and CentOS) and the `DEB` (for Debian and Ubuntu) packages are available from Percona repositories for PostgreSQL versions [12](https://www.percona.com/downloads/postgresql-distribution-12/LATEST/), [13](https://www.percona.com/downloads/postgresql-distribution-13/LATEST/), [14](https://www.percona.com/downloads/postgresql-distribution-14/LATEST/), [15](https://www.percona.com/downloads/postgresql-distribution-15/LATEST/) and [16](https://www.percona.com/downloads/postgresql-distribution-16/LATEST/). +The `RPM` (for RHEL and CentOS) and the `DEB` (for Debian and Ubuntu) packages are available from Percona repositories for PostgreSQL versions [12](https://www.percona.com/downloads/postgresql-distribution-12/LATEST/), [13](https://www.percona.com/downloads/postgresql-distribution-13/LATEST/), [14](https://www.percona.com/downloads/postgresql-distribution-14/LATEST/), [15](https://www.percona.com/downloads/postgresql-distribution-15/LATEST/), [16](https://www.percona.com/downloads/postgresql-distribution-16/LATEST/) and [17](https://www.percona.com/downloads/postgresql-distribution-17/LATEST/). The RPM packages are also available in the official PostgreSQL (PGDG) yum repositories. @@ -61,8 +62,8 @@ The `pg_stat_monitor` should work on the latest version of both [Percona Distrib | **Distribution** | **Version** | **Provider** | | ---------------- | --------------- | ------------ | -|[Percona Distribution for PostgreSQL](https://www.percona.com/software/postgresql-distribution)| [12](https://www.percona.com/downloads/postgresql-distribution-12/LATEST/), [13](https://www.percona.com/downloads/postgresql-distribution-13/LATEST/), [14](https://www.percona.com/downloads/postgresql-distribution-14/LATEST/), [15](https://www.percona.com/downloads/postgresql-distribution-15/LATEST/) and [16](https://www.percona.com/downloads/postgresql-distribution-16/LATEST/)| Percona| -| PostgreSQL | 12, 13, 14, 15 and 16 | PostgreSQL Global Development Group (PGDG) | +|[Percona Distribution for PostgreSQL](https://www.percona.com/software/postgresql-distribution)| [12](https://www.percona.com/downloads/postgresql-distribution-12/LATEST/), [13](https://www.percona.com/downloads/postgresql-distribution-13/LATEST/), [14](https://www.percona.com/downloads/postgresql-distribution-14/LATEST/), [15](https://www.percona.com/downloads/postgresql-distribution-15/LATEST/), [16](https://www.percona.com/downloads/postgresql-distribution-16/LATEST/) and [17](https://www.percona.com/downloads/postgresql-distribution-17/LATEST/)| Percona| +| PostgreSQL | 12, 13, 14, 15, 16 and 17 | PostgreSQL Global Development Group (PGDG) | ### Features @@ -121,16 +122,16 @@ To install `pg_stat_monitor` from Percona repositories, you need to use the `per percona-release setup ppg-XX ``` -Replace XX with the desired PostgreSQL version. For example, to install `pg_stat_monitor ` for PostgreSQL 16, specify `ppg-16`. +Replace XX with the desired PostgreSQL version. For example, to install `pg_stat_monitor ` for PostgreSQL 17, specify `ppg-17`. 3. Install `pg_stat_monitor` package * For Debian and Ubuntu: ``` sh - apt-get install percona-pg-stat-monitor16 + apt-get install percona-pg-stat-monitor17 ``` * For RHEL and CentOS: ``` sh - yum install percona-pg-stat-monitor16 + yum install percona-pg-stat-monitor17 ``` #### Installing from PostgreSQL `yum` repositories @@ -143,7 +144,7 @@ Install `pg_stat_monitor`: dnf install -y pg_stat_monitor_ ``` -Replace the `VERSION` variable with the PostgreSQL version you are using (e.g. specify `pg_stat_monitor_16` for PostgreSQL 16) +Replace the `VERSION` variable with the PostgreSQL version you are using (e.g. specify `pg_stat_monitor_17` for PostgreSQL 17) #### Installing from PGXN @@ -204,7 +205,7 @@ sudo systemctl restart postgresql.service ```sh -sudo systemctl restart postgresql-16 +sudo systemctl restart postgresql-17 ``` Create the extension using the [CREATE EXTENSION](https://www.postgresql.org/docs/current/sql-createextension.html) command. Using this command requires the privileges of a superuser or a database owner. Connect to `psql` as a superuser for a database and run the following command: @@ -280,7 +281,7 @@ To uninstall `pg_stat_monitor`, do the following: **Important**: If the `shared_preload_libraries` parameter includes other modules, specify them all for the `ALTER SYSTEM SET` command to keep using them. -3. Restart the `postgresql` instance to apply the changes. The following command restarts PostgreSQL 16. Replace the version value with the one you are using. +3. Restart the `postgresql` instance to apply the changes. The following command restarts PostgreSQL 17. Replace the version value with the one you are using. * On Debian and Ubuntu: @@ -292,7 +293,7 @@ To uninstall `pg_stat_monitor`, do the following: ```sh - sudo systemctl restart postgresql-16 + sudo systemctl restart postgresql-17 ``` ### How we work diff --git a/hash_query.c b/hash_query.c index ff34a4cf..c9021e36 100644 --- a/hash_query.c +++ b/hash_query.c @@ -303,6 +303,8 @@ hash_entry_alloc(pgsmSharedState * pgsm, pgsmHashKey * key, int encoding) memset(&entry->counters, 0, sizeof(Counters)); entry->query_text.query_pos = InvalidDsaPointer; entry->counters.info.parent_query = InvalidDsaPointer; + entry->stats_since = GetCurrentTimestamp(); + entry->minmax_stats_since = entry->stats_since; /* set the appropriate initial usage count */ /* re-initialize the mutex each time ... we assume no one using it */ diff --git a/pg_stat_monitor--2.0--2.1.sql b/pg_stat_monitor--2.0--2.1.sql new file mode 100644 index 00000000..ce60d596 --- /dev/null +++ b/pg_stat_monitor--2.0--2.1.sql @@ -0,0 +1,497 @@ +/* contrib/pg_stat_monitor/pg_stat_monitor--2.0--2.1.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 + +DROP FUNCTION pg_stat_monitor_internal CASCADE; +DROP FUNCTION pgsm_create_view CASCADE; +DROP FUNCTION pgsm_create_11_view(); +DROP FUNCTION pgsm_create_13_view(); +DROP FUNCTION pgsm_create_14_view(); +DROP FUNCTION pgsm_create_15_view(); + +CREATE FUNCTION pg_stat_monitor_internal( + IN showtext boolean, + OUT bucket int8, -- 0 + OUT userid oid, + OUT username text, + OUT dbid oid, + OUT datname text, + OUT client_ip int8, + + OUT queryid int8, -- 6 + OUT planid int8, + OUT query text, + OUT query_plan text, + OUT pgsm_query_id int8, + OUT top_queryid int8, + OUT top_query text, + OUT application_name text, + + OUT relations text, -- 14 + OUT cmd_type int, + OUT elevel int, + OUT sqlcode TEXT, + OUT message text, + OUT bucket_start_time timestamptz, + + OUT calls int8, -- 20 + + OUT total_exec_time float8, -- 21 + OUT min_exec_time float8, + OUT max_exec_time float8, + OUT mean_exec_time float8, + OUT stddev_exec_time float8, + + OUT rows int8, -- 26 + + OUT plans int8, -- 27 + + OUT total_plan_time float8, -- 28 + 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, -- 33 + 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 shared_blk_read_time float8, + OUT shared_blk_write_time float8, + OUT local_blk_read_time float8, + OUT local_blk_write_time float8, + OUT temp_blk_read_time float8, + OUT temp_blk_write_time float8, + + OUT resp_calls text, -- 49 + 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 jit_functions int8, -- 56 + 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 jit_deform_count int8, + OUT jit_deform_time float8, + + OUT stats_since timestamp with time zone, -- 66 + OUT minmax_stats_since timestamp with time zone, + + OUT toplevel BOOLEAN, -- 68 + OUT bucket_done BOOLEAN +) +RETURNS SETOF record +AS 'MODULE_PATHNAME', 'pg_stat_monitor_2_1' +LANGUAGE C STRICT VOLATILE PARALLEL SAFE; + +-- 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, + userid, + username, + dbid, + datname, + '0.0.0.0'::inet + client_ip AS client_ip, + pgsm_query_id, + 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 AS total_time, + min_exec_time AS min_time, + max_exec_time AS max_time, + mean_exec_time AS mean_time, + stddev_exec_time AS stddev_time, + rows, + 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, + shared_blk_read_time AS blk_read_time, + shared_blk_write_time AS blk_write_time, + (string_to_array(resp_calls, ',')) resp_calls, + cpu_user_time, + cpu_sys_time, + bucket_done +FROM pg_stat_monitor_internal(TRUE) +ORDER BY bucket_start_time; +RETURN 0; +END; +$$ LANGUAGE plpgsql; + + +CREATE FUNCTION pgsm_create_13_view() RETURNS INT AS +$$ +BEGIN +CREATE VIEW pg_stat_monitor AS SELECT + bucket, + bucket_start_time AS bucket_start_time, + userid, + username, + dbid, + datname, + '0.0.0.0'::inet + client_ip AS client_ip, + pgsm_query_id, + 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, + 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, + shared_blk_read_time AS blk_read_time, + shared_blk_write_time AS blk_write_time, + (string_to_array(resp_calls, ',')) resp_calls, + cpu_user_time, + cpu_sys_time, + wal_records, + wal_fpi, + wal_bytes, + bucket_done, + -- PostgreSQL-13 Specific Coulumns + plans, + total_plan_time, + min_plan_time, + max_plan_time, + mean_plan_time, + stddev_plan_time +FROM pg_stat_monitor_internal(TRUE) +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, + username, + dbid, + datname, + '0.0.0.0'::inet + client_ip AS client_ip, + pgsm_query_id, + 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, + 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, + shared_blk_read_time AS blk_read_time, + shared_blk_write_time AS 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, + total_plan_time, + min_plan_time, + max_plan_time, + mean_plan_time, + stddev_plan_time +FROM pg_stat_monitor_internal(TRUE) +ORDER BY bucket_start_time; +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, + username, + dbid, + datname, + '0.0.0.0'::inet + client_ip AS client_ip, + pgsm_query_id, + 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, + 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, + shared_blk_read_time AS blk_read_time, + shared_blk_write_time AS 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, + 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) +ORDER BY bucket_start_time; +RETURN 0; +END; +$$ LANGUAGE plpgsql; + +CREATE FUNCTION pgsm_create_17_view() RETURNS INT AS +$$ +BEGIN +CREATE VIEW pg_stat_monitor AS SELECT + bucket, + bucket_start_time, + userid, + username, + dbid, + datname, + '0.0.0.0'::inet + client_ip AS client_ip, + pgsm_query_id, + 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, + 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, + shared_blk_read_time, + shared_blk_write_time, + local_blk_read_time, + local_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, + 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, + jit_deform_count, + jit_deform_time, + + stats_since, + minmax_stats_since + +FROM pg_stat_monitor_internal(TRUE) +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 >= 170000) THEN + return pgsm_create_17_view(); + END IF; + IF (ver >= 150000) THEN + return pgsm_create_15_view(); + END IF; + IF (ver >= 140000) THEN + return pgsm_create_14_view(); + END IF; + IF (ver >= 130000) THEN + return pgsm_create_13_view(); + END IF; + IF (ver >= 110000) THEN + return pgsm_create_11_view(); + END IF; + RETURN 0; + END; +$$ LANGUAGE plpgsql; + +SELECT pgsm_create_view(); +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; +REVOKE ALL ON FUNCTION pgsm_create_17_view FROM PUBLIC; + +GRANT EXECUTE ON FUNCTION range TO PUBLIC; +GRANT EXECUTE ON FUNCTION decode_error_level TO PUBLIC; +GRANT EXECUTE ON FUNCTION get_histogram_timings TO PUBLIC; +GRANT EXECUTE ON FUNCTION get_cmd_type TO PUBLIC; +GRANT EXECUTE ON FUNCTION pg_stat_monitor_internal TO PUBLIC; + +GRANT SELECT ON pg_stat_monitor TO PUBLIC; + +-- Reset is only available to super user +REVOKE ALL ON FUNCTION pg_stat_monitor_reset FROM PUBLIC; diff --git a/pg_stat_monitor.c b/pg_stat_monitor.c index f8425450..bca75cf1 100644 --- a/pg_stat_monitor.c +++ b/pg_stat_monitor.c @@ -31,16 +31,18 @@ typedef enum pgsmVersion { PGSM_V1_0 = 0, - PGSM_V2_0 + PGSM_V2_0, + PGSM_V2_1 } pgsmVersion; PG_MODULE_MAGIC; -#define BUILD_VERSION "2.0.4" +#define BUILD_VERSION "2.1.0" /* Number of output arguments (columns) for various API versions */ #define PG_STAT_MONITOR_COLS_V1_0 52 #define PG_STAT_MONITOR_COLS_V2_0 64 +#define PG_STAT_MONITOR_COLS_V2_1 70 #define PG_STAT_MONITOR_COLS PG_STAT_MONITOR_COLS_V2_0 /* maximum of above */ #define PGSM_TEXT_FILE PGSTAT_STAT_PERMANENT_DIRECTORY "pg_stat_monitor_query" @@ -71,11 +73,12 @@ do \ /*---- Initicalization Function Declarations ----*/ void _PG_init(void); -/* Current nesting depth of ExecutorRun+ProcessUtility calls */ -static int exec_nested_level = 0; +/* Current nesting depth of planner/ExecutorRun/ProcessUtility calls */ +static int nesting_level = 0; volatile bool __pgsm_do_not_capture_error = false; -#if PG_VERSION_NUM >= 130000 +#if PG_VERSION_NUM >= 130000 && PG_VERSION_NUM < 170000 +/* Before planner nesting level was conunted separately */ static int plan_nested_level = 0; #endif @@ -145,6 +148,7 @@ 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_2_1); PG_FUNCTION_INFO_V1(pg_stat_monitor); PG_FUNCTION_INFO_V1(get_histogram_timings); PG_FUNCTION_INFO_V1(pg_stat_monitor_hook_stats); @@ -197,11 +201,6 @@ DECLARE_HOOK(void pgsm_ProcessUtility, PlannedStmt *pstmt, const char *queryStri static uint64 pgsm_hash_string(const char *str, int len); char *unpack_sql_state(int sql_state); -#define PGSM_HANDLED_UTILITY(n) (!IsA(n, ExecuteStmt) && \ - !IsA(n, PrepareStmt) && \ - !IsA(n, DeallocateStmt)) - - static pgsmEntry * pgsm_create_hash_entry(uint64 bucket_id, uint64 queryid, PlanInfo * plan_info); static void pgsm_add_to_list(pgsmEntry * entry, char *query_text, int query_len); static pgsmEntry * pgsm_get_entry_for_query(uint64 queryid, PlanInfo * plan_info, const char *query_text, int query_len, bool create); @@ -425,17 +424,18 @@ pgsm_post_parse_analyze_internal(ParseState *pstate, Query *query, JumbleState * } } - if (!pgsm_enabled(exec_nested_level)) + if (!pgsm_enabled(nesting_level)) return; /* - * Clear queryId for prepared statements related utility, as those will - * inherit from the underlying statement's one (except DEALLOCATE which is - * entirely untracked). + * If it's EXECUTE, clear the queryId so that stats will accumulate for + * the underlying PREPARE. But don't do this if we're not tracking + * utility statements, to avoid messing up another extension that might be + * tracking them. */ if (query->utilityStmt) { - if (pgsm_track_utility && !PGSM_HANDLED_UTILITY(query->utilityStmt)) + if (pgsm_track_utility && IsA(query->utilityStmt, ExecuteStmt)) query->queryId = UINT64CONST(0); return; @@ -568,7 +568,7 @@ pgsm_ExecutorStart(QueryDesc *queryDesc, int eflags) * counting of optimizable statements that are directly contained in * utility statements. */ - if (pgsm_enabled(exec_nested_level) && + if (pgsm_enabled(nesting_level) && queryDesc->plannedstmt->queryId != UINT64CONST(0)) { /* @@ -599,37 +599,37 @@ static void pgsm_ExecutorRun(QueryDesc *queryDesc, ScanDirection direction, uint64 count, bool execute_once) { - if (exec_nested_level >= 0 && exec_nested_level < max_stack_depth) + if (nesting_level >= 0 && nesting_level < max_stack_depth) { - nested_queryids[exec_nested_level] = queryDesc->plannedstmt->queryId; - nested_query_txts[exec_nested_level] = strdup(queryDesc->sourceText); + nested_queryids[nesting_level] = queryDesc->plannedstmt->queryId; + nested_query_txts[nesting_level] = strdup(queryDesc->sourceText); } - exec_nested_level++; + nesting_level++; PG_TRY(); { if (prev_ExecutorRun) prev_ExecutorRun(queryDesc, direction, count, execute_once); else standard_ExecutorRun(queryDesc, direction, count, execute_once); - exec_nested_level--; - if (exec_nested_level >= 0 && exec_nested_level < max_stack_depth) + nesting_level--; + if (nesting_level >= 0 && nesting_level < max_stack_depth) { - nested_queryids[exec_nested_level] = UINT64CONST(0); - if (nested_query_txts[exec_nested_level]) - free(nested_query_txts[exec_nested_level]); - nested_query_txts[exec_nested_level] = NULL; + nested_queryids[nesting_level] = UINT64CONST(0); + if (nested_query_txts[nesting_level]) + free(nested_query_txts[nesting_level]); + nested_query_txts[nesting_level] = NULL; } } PG_CATCH(); { - exec_nested_level--; - if (exec_nested_level >= 0 && exec_nested_level < max_stack_depth) + nesting_level--; + if (nesting_level >= 0 && nesting_level < max_stack_depth) { - nested_queryids[exec_nested_level] = UINT64CONST(0); - if (nested_query_txts[exec_nested_level]) - free(nested_query_txts[exec_nested_level]); - nested_query_txts[exec_nested_level] = NULL; + nested_queryids[nesting_level] = UINT64CONST(0); + if (nested_query_txts[nesting_level]) + free(nested_query_txts[nesting_level]); + nested_query_txts[nesting_level] = NULL; } PG_RE_THROW(); } @@ -642,7 +642,7 @@ pgsm_ExecutorRun(QueryDesc *queryDesc, ScanDirection direction, uint64 count, static void pgsm_ExecutorFinish(QueryDesc *queryDesc) { - exec_nested_level++; + nesting_level++; PG_TRY(); { @@ -650,11 +650,11 @@ pgsm_ExecutorFinish(QueryDesc *queryDesc) prev_ExecutorFinish(queryDesc); else standard_ExecutorFinish(queryDesc); - exec_nested_level--; + nesting_level--; } PG_CATCH(); { - exec_nested_level--; + nesting_level--; PG_RE_THROW(); } PG_END_TRY(); @@ -722,7 +722,7 @@ pgsm_ExecutorEnd(QueryDesc *queryDesc) MemoryContextSwitchTo(oldctx); } - if (queryId != UINT64CONST(0) && queryDesc->totaltime && pgsm_enabled(exec_nested_level)) + if (queryId != UINT64CONST(0) && queryDesc->totaltime && pgsm_enabled(nesting_level)) { entry = pgsm_get_entry_for_query(queryId, plan_ptr, (char *) queryDesc->sourceText, strlen(queryDesc->sourceText), true); if (!entry) @@ -868,8 +868,14 @@ pgsm_planner_hook(Query *parse, const char *query_string, int cursorOptions, Par * top level planner call. */ - if (pgsm_enabled(plan_nested_level + exec_nested_level) && - pgsm_track_planning && query_string && parse->queryId != UINT64CONST(0)) + bool enabled; +#if PG_VERSION_NUM >= 170000 + enabled = pgsm_enabled(nesting_level); +#else + enabled = pgsm_enabled(plan_nested_level + nesting_level); +#endif + + if (enabled && pgsm_track_planning && query_string && parse->queryId != UINT64CONST(0)) { pgsmEntry *entry = NULL; instr_time start; @@ -892,7 +898,11 @@ pgsm_planner_hook(Query *parse, const char *query_string, int cursorOptions, Par if (MemoryContextIsValid(MessageContext)) entry = pgsm_get_entry_for_query(parse->queryId, NULL, query_string, strlen(query_string), true); +#if PG_VERSION_NUM >= 170000 + nesting_level++; +#else plan_nested_level++; +#endif PG_TRY(); { /* @@ -909,7 +919,11 @@ pgsm_planner_hook(Query *parse, const char *query_string, int cursorOptions, Par } PG_FINALLY(); { +#if PG_VERSION_NUM >= 170000 + nesting_level--; +#else plan_nested_level--; +#endif } PG_END_TRY(); @@ -945,19 +959,38 @@ pgsm_planner_hook(Query *parse, const char *query_string, int cursorOptions, Par else { /* + * Even though we're not tracking plan time for this statement, we + * must still increment the nesting level, to ensure that functions + * evaluated during planning are not seen as top-level calls. + * * If there is a previous installed hook, then assume it's going to * call standard_planner() function, otherwise we call the function * here. This is to avoid calling standard_planner() function twice, * since it modifies the first argument (Query *), the second call * would trigger an assertion failure. */ - plan_nested_level++; - if (planner_hook_next) - result = planner_hook_next(parse, query_string, cursorOptions, boundParams); - else - result = standard_planner(parse, query_string, cursorOptions, boundParams); - plan_nested_level--; +#if PG_VERSION_NUM >= 170000 + nesting_level++; +#else + plan_nested_level++; +#endif + PG_TRY(); + { + if (planner_hook_next) + result = planner_hook_next(parse, query_string, cursorOptions, boundParams); + else + result = standard_planner(parse, query_string, cursorOptions, boundParams); + } + PG_FINALLY(); + { +#if PG_VERSION_NUM >= 170000 + nesting_level--; +#else + plan_nested_level--; +#endif + } + PG_END_TRY(); } return result; @@ -995,6 +1028,7 @@ pgsm_ProcessUtility(PlannedStmt *pstmt, const char *queryString, { Node *parsetree = pstmt->utilityStmt; uint64 queryId = 0; + bool enabled = pgsm_track_utility && pgsm_enabled(nesting_level); #if PG_VERSION_NUM < 140000 int len = strlen(queryString); @@ -1012,7 +1046,7 @@ pgsm_ProcessUtility(PlannedStmt *pstmt, const char *queryString, * since we are already measuring the statement's costs at the utility * level. */ - if (pgsm_track_utility && pgsm_enabled(exec_nested_level)) + if (enabled) pstmt->queryId = UINT64CONST(0); #endif @@ -1025,13 +1059,16 @@ pgsm_ProcessUtility(PlannedStmt *pstmt, const char *queryString, * hash table entry for the PREPARE (with hash calculated from the query * string), and then a different one with the same query string (but hash * calculated from the query tree) would be used to accumulate costs of - * ensuing EXECUTEs. This would be confusing, and inconsistent with other - * cases where planning time is not included at all. + * ensuing EXECUTEs. This would be confusing. Since PREPARE doesn't + * actually run the planner (only parse+rewrite), its costs are generally + * pretty negligible and it seems okay to just ignore it. * * Likewise, we don't track execution of DEALLOCATE. */ - if (pgsm_track_utility && pgsm_enabled(exec_nested_level) && - PGSM_HANDLED_UTILITY(parsetree)) + if (enabled && + !IsA(parsetree, ExecuteStmt) && + !IsA(parsetree, PrepareStmt) && + !IsA(parsetree, DeallocateStmt)) { pgsmEntry *entry; char *query_text; @@ -1052,7 +1089,7 @@ pgsm_ProcessUtility(PlannedStmt *pstmt, const char *queryString, elog(DEBUG1, "[pg_stat_monitor] pgsm_ProcessUtility: Failed to execute getrusage."); INSTR_TIME_SET_CURRENT(start); - exec_nested_level++; + nesting_level++; PG_TRY(); { @@ -1092,11 +1129,11 @@ pgsm_ProcessUtility(PlannedStmt *pstmt, const char *queryString, dest, completionTag); #endif - exec_nested_level--; + nesting_level--; } PG_CATCH(); { - exec_nested_level--; + nesting_level--; PG_RE_THROW(); } @@ -1182,41 +1219,80 @@ pgsm_ProcessUtility(PlannedStmt *pstmt, const char *queryString, } else { + /* + * Even though we're not tracking execution time for this statement, + * we must still increment the nesting level, to ensure that functions + * evaluated within it are not seen as top-level calls. But don't do + * so for EXECUTE; that way, when control reaches pgss_planner or + * pgss_ExecutorStart, we will treat the costs as top-level if + * appropriate. Likewise, don't bump for PREPARE, so that parse + * analysis will treat the statement as top-level if appropriate. + * + * Likewise, we don't track execution of DEALLOCATE. + * + * To be absolutely certain we don't mess up the nesting level, + * evaluate the bump_level condition just once. + */ + +#if PG_VERSION_NUM >= 170000 + bool bump_level = + !IsA(parsetree, ExecuteStmt) && + !IsA(parsetree, PrepareStmt) && + !IsA(parsetree, DeallocateStmt); + + if (bump_level) + nesting_level++; + + PG_TRY(); + { +#endif #if PG_VERSION_NUM >= 140000 - if (prev_ProcessUtility) - prev_ProcessUtility(pstmt, queryString, - readOnlyTree, - context, params, queryEnv, - dest, - qc); - else - standard_ProcessUtility(pstmt, queryString, + if (prev_ProcessUtility) + prev_ProcessUtility(pstmt, queryString, readOnlyTree, context, params, queryEnv, dest, qc); + else + standard_ProcessUtility(pstmt, queryString, + readOnlyTree, + context, params, queryEnv, + dest, + qc); #elif PG_VERSION_NUM >= 130000 - if (prev_ProcessUtility) - prev_ProcessUtility(pstmt, queryString, - context, params, queryEnv, - dest, - qc); - else - standard_ProcessUtility(pstmt, queryString, + if (prev_ProcessUtility) + prev_ProcessUtility(pstmt, queryString, context, params, queryEnv, dest, qc); + else + standard_ProcessUtility(pstmt, queryString, + context, params, queryEnv, + dest, + qc); #else - if (prev_ProcessUtility) - prev_ProcessUtility(pstmt, queryString, - context, params, queryEnv, - dest, - completionTag); - else - standard_ProcessUtility(pstmt, queryString, + if (prev_ProcessUtility) + prev_ProcessUtility(pstmt, queryString, context, params, queryEnv, dest, completionTag); + else + standard_ProcessUtility(pstmt, queryString, + context, params, queryEnv, + dest, + completionTag); +#endif +#if PG_VERSION_NUM >= 170000 + if (bump_level) + nesting_level--; + } + PG_CATCH(); + { + if (bump_level) + nesting_level--; + PG_RE_THROW(); + } + PG_END_TRY(); #endif } } @@ -1358,9 +1434,13 @@ pgsm_update_entry(pgsmEntry * entry, int sqlcode_len = error_info ? strlen(error_info->sqlcode) : 0; int plan_text_len = plan_info ? plan_info->plan_len : 0; - /* Start collecting data for next bucket and reset all counters */ + /* Start collecting data for next bucket and reset all counters and timestamps */ if (reset) + { memset(&entry->counters, 0, sizeof(Counters)); + entry->stats_since = GetCurrentTimestamp(); + entry->minmax_stats_since = entry->stats_since; + } /* volatile block */ { @@ -1457,14 +1537,14 @@ pgsm_update_entry(pgsmEntry * entry, e->counters.info.num_relations = num_relations; _snprintf2(e->counters.info.relations, relations, num_relations, REL_LEN); - if (exec_nested_level > 0 && e->counters.info.parentid == 0 && pgsm_track == PGSM_TRACK_ALL) + if (nesting_level > 0 && e->counters.info.parentid == 0 && pgsm_track == PGSM_TRACK_ALL) { - if (exec_nested_level >= 0 && exec_nested_level < max_stack_depth) + if (nesting_level >= 0 && nesting_level < max_stack_depth) { - int parent_query_len = nested_query_txts[exec_nested_level - 1] ? - strlen(nested_query_txts[exec_nested_level - 1]) : 0; + int parent_query_len = nested_query_txts[nesting_level - 1] ? + strlen(nested_query_txts[nesting_level - 1]) : 0; - e->counters.info.parentid = nested_queryids[exec_nested_level - 1]; + e->counters.info.parentid = nested_queryids[nesting_level - 1]; e->counters.info.parent_query = InvalidDsaPointer; /* If we have a parent query, store it in the raw dsa area */ if (parent_query_len > 0) @@ -1482,7 +1562,7 @@ pgsm_update_entry(pgsmEntry * entry, if (DsaPointerIsValid(qry)) { qry_buff = dsa_get_address(query_dsa_area, qry); - memcpy(qry_buff, nested_query_txts[exec_nested_level - 1], parent_query_len); + memcpy(qry_buff, nested_query_txts[nesting_level - 1], parent_query_len); qry_buff[parent_query_len] = 0; /* store the dsa pointer for parent query text */ e->counters.info.parent_query = qry; @@ -1518,15 +1598,32 @@ pgsm_update_entry(pgsmEntry * entry, e->counters.blocks.local_blks_written += bufusage->local_blks_written; e->counters.blocks.temp_blks_read += bufusage->temp_blks_read; 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 < 170000 + e->counters.blocks.shared_blk_read_time += INSTR_TIME_GET_MILLISEC(bufusage->blk_read_time); + e->counters.blocks.shared_blk_write_time += INSTR_TIME_GET_MILLISEC(bufusage->blk_write_time); +#else + e->counters.blocks.shared_blk_read_time += INSTR_TIME_GET_MILLISEC(bufusage->shared_blk_read_time); + e->counters.blocks.shared_blk_write_time += INSTR_TIME_GET_MILLISEC(bufusage->shared_blk_write_time); + e->counters.blocks.local_blk_read_time += INSTR_TIME_GET_MILLISEC(bufusage->local_blk_read_time); + e->counters.blocks.local_blk_write_time += INSTR_TIME_GET_MILLISEC(bufusage->local_blk_write_time); +#endif + #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 - memcpy((void *) &e->counters.blocks.instr_blk_read_time, &bufusage->blk_read_time, sizeof(instr_time)); - memcpy((void *) &e->counters.blocks.instr_blk_write_time, &bufusage->blk_write_time, sizeof(instr_time)); +#if PG_VERSION_NUM < 170000 + memcpy((void *) &e->counters.blocks.instr_shared_blk_read_time, &bufusage->blk_read_time, sizeof(instr_time)); + memcpy((void *) &e->counters.blocks.instr_shared_blk_write_time, &bufusage->blk_write_time, sizeof(instr_time)); +#else + memcpy((void *) &e->counters.blocks.instr_shared_blk_read_time, &bufusage->shared_blk_read_time, sizeof(instr_time)); + memcpy((void *) &e->counters.blocks.instr_shared_blk_write_time, &bufusage->shared_blk_write_time, sizeof(instr_time)); + memcpy((void *) &e->counters.blocks.instr_local_blk_write_time, &bufusage->local_blk_write_time, sizeof(instr_time)); + memcpy((void *) &e->counters.blocks.instr_local_blk_write_time, &bufusage->local_blk_write_time, sizeof(instr_time)); +#endif + #if PG_VERSION_NUM >= 150000 memcpy((void *) &e->counters.blocks.instr_temp_blk_read_time, &bufusage->temp_blk_read_time, sizeof(bufusage->temp_blk_read_time)); @@ -1564,6 +1661,12 @@ pgsm_update_entry(pgsmEntry * entry, e->counters.jitinfo.jit_emission_count++; e->counters.jitinfo.jit_emission_time += INSTR_TIME_GET_MILLISEC(jitusage->emission_counter); +#if PG_VERSION_NUM >= 170000 + if (INSTR_TIME_GET_MILLISEC(jitusage->deform_counter)) + e->counters.jitinfo.jit_deform_count++; + e->counters.jitinfo.jit_deform_time += INSTR_TIME_GET_MILLISEC(jitusage->deform_counter); +#endif + /* Only do this for local storage scenarios */ if (kind != PGSM_STORE) { @@ -1571,6 +1674,10 @@ pgsm_update_entry(pgsmEntry * entry, memcpy((void *) &e->counters.jitinfo.instr_inlining_counter, &jitusage->inlining_counter, sizeof(instr_time)); memcpy((void *) &e->counters.jitinfo.instr_optimization_counter, &jitusage->optimization_counter, sizeof(instr_time)); memcpy((void *) &e->counters.jitinfo.instr_emission_counter, &jitusage->emission_counter, sizeof(instr_time)); + +#if PG_VERSION_NUM >= 170000 + memcpy((void *) &e->counters.jitinfo.instr_deform_counter, &jitusage->deform_counter, sizeof(instr_time)); +#endif } } @@ -1716,7 +1823,11 @@ pgsm_create_hash_entry(uint64 bucket_id, uint64 queryid, PlanInfo * plan_info) #if PG_VERSION_NUM < 140000 entry->key.toplevel = 1; #else - entry->key.toplevel = ((exec_nested_level + plan_nested_level) == 0); +#if PG_VERSION_NUM >= 170000 + entry->key.toplevel = ((nesting_level) == 0); +#else + entry->key.toplevel = ((nesting_level + plan_nested_level) == 0); +#endif #endif if (IsTransactionState()) @@ -1802,8 +1913,15 @@ pgsm_store(pgsmEntry * entry) bufusage.temp_blks_read = entry->counters.blocks.temp_blks_read; bufusage.temp_blks_written = entry->counters.blocks.temp_blks_written; - memcpy(&bufusage.blk_read_time, &entry->counters.blocks.instr_blk_read_time, sizeof(instr_time)); - memcpy(&bufusage.blk_write_time, &entry->counters.blocks.instr_blk_write_time, sizeof(instr_time)); +#if PG_VERSION_NUM < 170000 + memcpy(&bufusage.blk_read_time, &entry->counters.blocks.instr_shared_blk_read_time, sizeof(instr_time)); + memcpy(&bufusage.blk_write_time, &entry->counters.blocks.instr_shared_blk_write_time, sizeof(instr_time)); +#else + memcpy(&bufusage.shared_blk_read_time, &entry->counters.blocks.instr_shared_blk_read_time, sizeof(instr_time)); + memcpy(&bufusage.shared_blk_write_time, &entry->counters.blocks.instr_shared_blk_write_time, sizeof(instr_time)); + memcpy(&bufusage.local_blk_read_time, &entry->counters.blocks.instr_local_blk_read_time, sizeof(instr_time)); + memcpy(&bufusage.local_blk_write_time, &entry->counters.blocks.instr_local_blk_write_time, sizeof(instr_time)); +#endif #if PG_VERSION_NUM >= 150000 memcpy(&bufusage.temp_blk_read_time, &entry->counters.blocks.instr_temp_blk_read_time, sizeof(instr_time)); @@ -1822,6 +1940,10 @@ pgsm_store(pgsmEntry * entry) memcpy(&jitusage.optimization_counter, &entry->counters.jitinfo.instr_optimization_counter, sizeof(instr_time)); memcpy(&jitusage.emission_counter, &entry->counters.jitinfo.instr_emission_counter, sizeof(instr_time)); +#if PG_VERSION_NUM >= 170000 + memcpy(&jitusage.deform_counter, &entry->counters.jitinfo.instr_deform_counter, sizeof(instr_time)); +#endif + /* * Acquire a share lock to start with. We'd have to acquire exclusive if * we need ot create the entry. @@ -1979,6 +2101,13 @@ pg_stat_monitor_2_0(PG_FUNCTION_ARGS) return (Datum) 0; } +Datum +pg_stat_monitor_2_1(PG_FUNCTION_ARGS) +{ + pg_stat_monitor_internal(fcinfo, PGSM_V2_1, true); + return (Datum) 0; +} + /* * Legacy entry point for pg_stat_monitor() API versions 1.0 */ @@ -1999,7 +2128,7 @@ IsBucketValid(uint64 bucketid) TimestampDifference(pgsm->bucket_start_time[bucketid], current_tz, &secs, µsecs); - if (secs > ((int64)pgsm_bucket_time * pgsm_max_buckets)) + if (secs > ((int64) pgsm_bucket_time * pgsm_max_buckets)) return false; return true; } @@ -2018,7 +2147,25 @@ pg_stat_monitor_internal(FunctionCallInfo fcinfo, PGSM_HASH_SEQ_STATUS hstat; pgsmEntry *entry; pgsmSharedState *pgsm; - int expected_columns = (api_version >= PGSM_V2_0) ? PG_STAT_MONITOR_COLS_V2_0 : PG_STAT_MONITOR_COLS_V1_0; + + int expected_columns; + + switch (api_version) + { + case PGSM_V1_0: + expected_columns = PG_STAT_MONITOR_COLS_V1_0; + break; + case PGSM_V2_0: + expected_columns = PG_STAT_MONITOR_COLS_V2_0; + break; + case PGSM_V2_1: + expected_columns = PG_STAT_MONITOR_COLS_V2_1; + break; + default: + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("[pg_stat_monitor] pg_stat_monitor_internal: Unknown API version"))); + } /* Disallow old api usage */ if (api_version < PGSM_V2_0) @@ -2148,17 +2295,17 @@ pg_stat_monitor_internal(FunctionCallInfo fcinfo, /* userid at column number 1 */ values[i++] = ObjectIdGetDatum(userid); - /* userid at column number 1 */ + /* username at column number 2 */ values[i++] = CStringGetTextDatum(entry->username); - /* dbid at column number 2 */ + /* dbid at column number 3 */ values[i++] = ObjectIdGetDatum(dbid); - /* userid at column number 1 */ + /* datname at column number 4 */ values[i++] = CStringGetTextDatum(entry->datname); /* - * ip address at column number 3, Superusers or members of + * ip address at column number 5, Superusers or members of * pg_read_all_stats members are allowed */ if (is_allowed_role || userid == GetUserId()) @@ -2166,10 +2313,10 @@ pg_stat_monitor_internal(FunctionCallInfo fcinfo, else nulls[i++] = true; - /* queryid at column number 4 */ + /* queryid at column number 6 */ values[i++] = UInt64GetDatum(queryid); - /* planid at column number 5 */ + /* planid at column number 7 */ if (planid) { values[i++] = UInt64GetDatum(planid); @@ -2184,12 +2331,12 @@ pg_stat_monitor_internal(FunctionCallInfo fcinfo, { char *enc; - /* query at column number 6 */ + /* query at column number 8 */ enc = pg_any_to_server(query_txt, strlen(query_txt), GetDatabaseEncoding()); values[i++] = CStringGetTextDatum(enc); if (enc != query_txt) pfree(enc); - /* plan at column number 7 */ + /* plan at column number 9 */ if (planid && tmp.planinfo.plan_text[0]) values[i++] = CStringGetTextDatum(tmp.planinfo.plan_text); else @@ -2197,25 +2344,26 @@ pg_stat_monitor_internal(FunctionCallInfo fcinfo, } else { - /* query at column number 6 */ + /* query at column number 8 */ nulls[i++] = true; - /* plan at column number 7 */ + /* plan at column number 9 */ nulls[i++] = true; } } else { - /* query text at column number 6 */ + /* query text and plan at column number 8 and 9 */ values[i++] = CStringGetTextDatum(""); values[i++] = CStringGetTextDatum(""); } + /* pgsm_query_id at column number 10 */ if (pgsm_query_id) values[i++] = UInt64GetDatum(pgsm_query_id); else nulls[i++] = true; - /* parentid at column number 9 */ + /* parentid at column number 11 */ if (tmp.info.parentid != UINT64CONST(0)) { values[i++] = UInt64GetDatum(tmp.info.parentid); @@ -2227,13 +2375,13 @@ pg_stat_monitor_internal(FunctionCallInfo fcinfo, nulls[i++] = true; } - /* application_name at column number 10 */ + /* application_name at column number 15 */ if (strlen(tmp.info.application_name) > 0) values[i++] = CStringGetTextDatum(tmp.info.application_name); else nulls[i++] = true; - /* relations at column number 10 */ + /* relations at column number 14 */ if (tmp.info.num_relations > 0) { int j; @@ -2262,28 +2410,28 @@ pg_stat_monitor_internal(FunctionCallInfo fcinfo, else nulls[i++] = true; - /* cmd_type at column number 11 */ + /* cmd_type at column number 15 */ if (tmp.info.cmd_type == CMD_NOTHING) nulls[i++] = true; else values[i++] = Int64GetDatumFast((int64) tmp.info.cmd_type); - /* elevel at column number 12 */ + /* elevel at column number 16 */ values[i++] = Int64GetDatumFast(tmp.error.elevel); - /* sqlcode at column number 13 */ + /* sqlcode at column number 17 */ if (strlen(tmp.error.sqlcode) == 0) nulls[i++] = true; else values[i++] = CStringGetTextDatum(tmp.error.sqlcode); - /* message at column number 14 */ + /* message at column number 18 */ if (strlen(tmp.error.message) == 0) nulls[i++] = true; else values[i++] = CStringGetTextDatum(tmp.error.message); - /* bucket_start_time at column number 15 */ + /* bucket_start_time at column number 19 */ values[i++] = TimestampTzGetDatum(pgsm->bucket_start_time[entry->key.bucket_id]); if (tmp.calls.calls == 0) @@ -2293,29 +2441,29 @@ pg_stat_monitor_internal(FunctionCallInfo fcinfo, tmp.resp_calls[0]++; } - /* calls at column number 16 */ + /* calls at column number 20 */ values[i++] = Int64GetDatumFast(tmp.calls.calls); - /* total_time at column number 17 */ + /* total_time at column number 21 */ values[i++] = Float8GetDatumFast(tmp.time.total_time); - /* min_time at column number 18 */ + /* min_time at column number 22 */ values[i++] = Float8GetDatumFast(tmp.time.min_time); - /* max_time at column number 19 */ + /* max_time at column number 23 */ values[i++] = Float8GetDatumFast(tmp.time.max_time); - /* mean_time at column number 20 */ + /* mean_time at column number 24 */ values[i++] = Float8GetDatumFast(tmp.time.mean_time); if (tmp.calls.calls > 1) stddev = sqrt(tmp.time.sum_var_time / tmp.calls.calls); else stddev = 0.0; - /* calls at column number 21 */ + /* stddev_exec_time at column number 25 */ values[i++] = Float8GetDatumFast(stddev); - /* calls at column number 22 */ + /* rows at column number 26 */ values[i++] = Int64GetDatumFast(tmp.calls.rows); if (tmp.calls.calls == 0) @@ -2325,29 +2473,29 @@ pg_stat_monitor_internal(FunctionCallInfo fcinfo, tmp.resp_calls[0]++; } - /* calls at column number 23 */ + /* plans at column number 27 */ values[i++] = Int64GetDatumFast(tmp.plancalls.calls); - /* total_time at column number 24 */ + /* total_plan_time at column number 28 */ values[i++] = Float8GetDatumFast(tmp.plantime.total_time); - /* min_time at column number 25 */ + /* min_plan_time at column number 29 */ values[i++] = Float8GetDatumFast(tmp.plantime.min_time); - /* max_time at column number 26 */ + /* max_plan_time at column number 30 */ values[i++] = Float8GetDatumFast(tmp.plantime.max_time); - /* mean_time at column number 27 */ + /* mean_plan_time at column number 31 */ values[i++] = Float8GetDatumFast(tmp.plantime.mean_time); if (tmp.plancalls.calls > 1) stddev = sqrt(tmp.plantime.sum_var_time / tmp.plancalls.calls); else stddev = 0.0; - /* calls at column number 28 */ + /* stddev_plan_time at column number 32 */ values[i++] = Float8GetDatumFast(stddev); - /* blocks are from column number 29 - 40 */ + /* blocks are from column number 33 - 48 */ values[i++] = Int64GetDatumFast(tmp.blocks.shared_blks_hit); values[i++] = Int64GetDatumFast(tmp.blocks.shared_blks_read); values[i++] = Int64GetDatumFast(tmp.blocks.shared_blks_dirtied); @@ -2358,27 +2506,29 @@ pg_stat_monitor_internal(FunctionCallInfo fcinfo, values[i++] = Int64GetDatumFast(tmp.blocks.local_blks_written); values[i++] = Int64GetDatumFast(tmp.blocks.temp_blks_read); values[i++] = Int64GetDatumFast(tmp.blocks.temp_blks_written); - values[i++] = Float8GetDatumFast(tmp.blocks.blk_read_time); - values[i++] = Float8GetDatumFast(tmp.blocks.blk_write_time); + values[i++] = Float8GetDatumFast(tmp.blocks.shared_blk_read_time); + values[i++] = Float8GetDatumFast(tmp.blocks.shared_blk_write_time); + values[i++] = Float8GetDatumFast(tmp.blocks.local_blk_read_time); + values[i++] = Float8GetDatumFast(tmp.blocks.local_blk_write_time); values[i++] = Float8GetDatumFast(tmp.blocks.temp_blk_read_time); values[i++] = Float8GetDatumFast(tmp.blocks.temp_blk_write_time); - /* resp_calls at column number 41 */ + /* resp_calls at column number 49 */ values[i++] = IntArrayGetTextDatum(tmp.resp_calls, hist_bucket_count_total); - /* utime at column number 42 */ + /* cpu_user_time at column number 50 */ values[i++] = Float8GetDatumFast(tmp.sysinfo.utime); - /* stime at column number 43 */ + /* cpu_sys_time at column number 51 */ values[i++] = Float8GetDatumFast(tmp.sysinfo.stime); { char buf[256]; Datum wal_bytes; - /* wal_records at column number 44 */ + /* wal_records at column number 52 */ values[i++] = Int64GetDatumFast(tmp.walusage.wal_records); - /* wal_fpi at column number 45 */ + /* wal_fpi at column number 53 */ values[i++] = Int64GetDatumFast(tmp.walusage.wal_fpi); snprintf(buf, sizeof buf, UINT64_FORMAT, tmp.walusage.wal_bytes); @@ -2388,15 +2538,16 @@ pg_stat_monitor_internal(FunctionCallInfo fcinfo, CStringGetDatum(buf), ObjectIdGetDatum(0), Int32GetDatum(-1)); - /* wal_bytes at column number 46 */ + /* wal_bytes at column number 54 */ values[i++] = wal_bytes; - /* application_name at column number 47 */ + /* application_name at column number 55 */ if (strlen(tmp.info.comments) > 0) values[i++] = CStringGetTextDatum(tmp.info.comments); else nulls[i++] = true; + /* blocks are from column number 56 - 63 */ values[i++] = Int64GetDatumFast(tmp.jitinfo.jit_functions); values[i++] = Float8GetDatumFast(tmp.jitinfo.jit_generation_time); values[i++] = Int64GetDatumFast(tmp.jitinfo.jit_inlining_count); @@ -2405,8 +2556,18 @@ pg_stat_monitor_internal(FunctionCallInfo fcinfo, 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++] = Int64GetDatumFast(tmp.jitinfo.jit_deform_count); + values[i++] = Float8GetDatumFast(tmp.jitinfo.jit_deform_time); } + + /* at column number 64 */ + values[i++] = TimestampTzGetDatum(entry->stats_since); + values[i++] = TimestampTzGetDatum(entry->minmax_stats_since); + + /* toplevel at column number 66 */ values[i++] = BoolGetDatum(toplevel); + + /* bucket_done at column number 67 */ values[i++] = BoolGetDatum(pg_atomic_read_u64(&pgsm->current_wbucket) != bucketid); /* clean up and return the tuplestore */ @@ -2420,9 +2581,6 @@ pg_stat_monitor_internal(FunctionCallInfo fcinfo, /* clean up and return the tuplestore */ pgsm_hash_seq_term(&hstat); LWLockRelease(pgsm->lock); - - - tuplestore_donestoring(tupstore); } static uint64 diff --git a/pg_stat_monitor.control b/pg_stat_monitor.control index c7651311..b52a4de1 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 = '2.0' +default_version = '2.1' module_pathname = '$libdir/pg_stat_monitor' relocatable = true diff --git a/pg_stat_monitor.h b/pg_stat_monitor.h index a6ccc450..4d215369 100644 --- a/pg_stat_monitor.h +++ b/pg_stat_monitor.h @@ -274,31 +274,33 @@ typedef struct Calls typedef struct Blocks { - int64 shared_blks_hit; /* # of shared buffer hits */ - int64 shared_blks_read; /* # of shared disk blocks read */ + int64 shared_blks_hit; /* # of shared buffer hits */ + int64 shared_blks_read; /* # of shared disk blocks read */ int64 shared_blks_dirtied; /* # of shared disk blocks dirtied */ int64 shared_blks_written; /* # of shared disk blocks written */ - int64 local_blks_hit; /* # of local buffer hits */ - int64 local_blks_read; /* # of local disk blocks read */ - int64 local_blks_dirtied; /* # of local disk blocks dirtied */ - int64 local_blks_written; /* # of local disk blocks written */ - int64 temp_blks_read; /* # of temp blocks read */ - 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 */ + int64 local_blks_hit; /* # of local buffer hits */ + int64 local_blks_read; /* # of local disk blocks read */ + int64 local_blks_dirtied; /* # of local disk blocks dirtied */ + int64 local_blks_written; /* # of local disk blocks written */ + int64 temp_blks_read; /* # of temp blocks read */ + int64 temp_blks_written; /* # of temp blocks written */ + double shared_blk_read_time; /* time spent reading shared blocks, in msec */ + double shared_blk_write_time; /* time spent writing shared blocks, in msec */ + double local_blk_read_time; /* time spent reading local blocks, in msec */ + double local_blk_write_time; /* time spent writing local blocks, 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 */ /* * Variables for local entry. The values to be passed to pgsm_update_entry * from pgsm_store. */ - instr_time instr_blk_read_time; /* time spent reading blocks */ - instr_time instr_blk_write_time; /* time spent writing blocks */ - instr_time instr_temp_blk_read_time; /* time spent reading temp blocks */ - instr_time instr_temp_blk_write_time; /* time spent writing temp blocks */ + instr_time instr_shared_blk_read_time; /* time spent reading shared blocks */ + instr_time instr_shared_blk_write_time; /* time spent writing shared blocks */ + instr_time instr_local_blk_read_time; /* time spent reading local blocks */ + instr_time instr_local_blk_write_time; /* time spent writing local blocks */ + instr_time instr_temp_blk_read_time; /* time spent reading temp blocks */ + instr_time instr_temp_blk_write_time; /* time spent writing temp blocks */ } Blocks; typedef struct JitInfo @@ -307,6 +309,9 @@ typedef struct JitInfo double jit_generation_time; /* total time to generate jit code */ int64 jit_inlining_count; /* number of times inlining time has been * > 0 */ + double jit_deform_time; /* total time to deform tuples in jit code */ + int64 jit_deform_count; /* number of times deform 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 */ @@ -321,6 +326,7 @@ typedef struct JitInfo */ instr_time instr_generation_counter; /* generation counter */ instr_time instr_inlining_counter; /* inlining counter */ + instr_time instr_deform_counter; /* deform counter */ instr_time instr_optimization_counter; /* optimization counter */ instr_time instr_emission_counter; /* emission counter */ } JitInfo; @@ -370,6 +376,8 @@ typedef struct pgsmEntry char username[NAMEDATALEN]; /* user name */ Counters counters; /* the statistics for this query */ int encoding; /* query text encoding */ + TimestampTz stats_since; /* timestamp of entry allocation */ + TimestampTz minmax_stats_since; /* timestamp of last min/max values reset */ slock_t mutex; /* protects the counters only */ union { diff --git a/regression/expected/functions.out b/regression/expected/functions.out index b924c8d9..b6cd9530 100644 --- a/regression/expected/functions.out +++ b/regression/expected/functions.out @@ -24,9 +24,10 @@ SELECT routine_schema, routine_name, routine_type, data_type FROM information_sc public | pgsm_create_13_view | FUNCTION | integer public | pgsm_create_14_view | FUNCTION | integer public | pgsm_create_15_view | FUNCTION | integer + public | pgsm_create_17_view | FUNCTION | integer public | pgsm_create_view | FUNCTION | integer public | range | FUNCTION | ARRAY -(13 rows) +(14 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"; diff --git a/regression/expected/functions_1.out b/regression/expected/functions_1.out index 2e3fccad..2acb2f9e 100644 --- a/regression/expected/functions_1.out +++ b/regression/expected/functions_1.out @@ -24,9 +24,10 @@ SELECT routine_schema, routine_name, routine_type, data_type FROM information_sc public | pgsm_create_13_view | FUNCTION | integer public | pgsm_create_14_view | FUNCTION | integer public | pgsm_create_15_view | FUNCTION | integer + public | pgsm_create_17_view | FUNCTION | integer public | pgsm_create_view | FUNCTION | integer public | range | FUNCTION | ARRAY -(13 rows) +(14 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"; diff --git a/regression/expected/level_tracking.out b/regression/expected/level_tracking.out new file mode 100644 index 00000000..e44c7673 --- /dev/null +++ b/regression/expected/level_tracking.out @@ -0,0 +1,6 @@ +-- +-- Statement level tracking +-- +SELECT setting::integer < 140000 AS skip_test FROM pg_settings where name = 'server_version_num' \gset +\if :skip_test +\quit diff --git a/regression/expected/level_tracking_1.out b/regression/expected/level_tracking_1.out new file mode 100644 index 00000000..8b595868 --- /dev/null +++ b/regression/expected/level_tracking_1.out @@ -0,0 +1,326 @@ +-- +-- Statement level tracking +-- +SELECT setting::integer < 140000 AS skip_test FROM pg_settings where name = 'server_version_num' \gset +\if :skip_test +\quit +\endif +CREATE EXTENSION pg_stat_monitor; +SET pg_stat_monitor.pgsm_track_utility = TRUE; +SET pg_stat_monitor.pgsm_normalized_query = TRUE; +SELECT pg_stat_monitor_reset(); + pg_stat_monitor_reset +----------------------- + +(1 row) + +-- DO block - top-level tracking. +CREATE TABLE stats_track_tab (x int); +SET pg_stat_monitor.pgsm_track = 'top'; +DELETE FROM stats_track_tab; +DO $$ +BEGIN + DELETE FROM stats_track_tab; +END; +$$ LANGUAGE plpgsql; +SELECT toplevel, calls, query FROM pg_stat_monitor + WHERE query LIKE '%DELETE%' ORDER BY query COLLATE "C", toplevel; + toplevel | calls | query +----------+-------+-------------------------------- + t | 1 | DELETE FROM stats_track_tab + t | 1 | DO $$ + + | | BEGIN + + | | DELETE FROM stats_track_tab;+ + | | END; + + | | $$ LANGUAGE plpgsql +(2 rows) + +SELECT pg_stat_monitor_reset(); + pg_stat_monitor_reset +----------------------- + +(1 row) + +-- DO block - all-level tracking. +SET pg_stat_monitor.pgsm_track = 'all'; +DELETE FROM stats_track_tab; +DO $$ +BEGIN + DELETE FROM stats_track_tab; +END; $$; +DO LANGUAGE plpgsql $$ +BEGIN + -- this is a SELECT + PERFORM 'hello world'::TEXT; +END; $$; +SELECT toplevel, calls, query FROM pg_stat_monitor + ORDER BY query COLLATE "C", toplevel; + toplevel | calls | query +----------+-------+---------------------------------------- + f | 1 | DELETE FROM stats_track_tab + t | 1 | DELETE FROM stats_track_tab + t | 1 | DO $$ + + | | BEGIN + + | | DELETE FROM stats_track_tab; + + | | END; $$ + t | 1 | DO LANGUAGE plpgsql $$ + + | | BEGIN + + | | -- this is a SELECT + + | | PERFORM 'hello world'::TEXT; + + | | END; $$ + f | 1 | SELECT $1::TEXT + t | 1 | SELECT pg_stat_monitor_reset() + t | 1 | SET pg_stat_monitor.pgsm_track = 'all' +(7 rows) + +-- DO block - top-level tracking without utility. +SET pg_stat_monitor.pgsm_track = 'top'; +SET pg_stat_monitor.pgsm_track_utility = FALSE; +SELECT pg_stat_monitor_reset(); + pg_stat_monitor_reset +----------------------- + +(1 row) + +DELETE FROM stats_track_tab; +DO $$ +BEGIN + DELETE FROM stats_track_tab; +END; $$; +DO LANGUAGE plpgsql $$ +BEGIN + -- this is a SELECT + PERFORM 'hello world'::TEXT; +END; $$; +SELECT toplevel, calls, query FROM pg_stat_monitor + ORDER BY query COLLATE "C", toplevel; + toplevel | calls | query +----------+-------+-------------------------------- + t | 2 | DELETE FROM stats_track_tab + t | 1 | SELECT $1::TEXT + t | 1 | SELECT pg_stat_monitor_reset() +(3 rows) + +-- DO block - all-level tracking without utility. +SET pg_stat_monitor.pgsm_track = 'all'; +SELECT pg_stat_monitor_reset(); + pg_stat_monitor_reset +----------------------- + +(1 row) + +DELETE FROM stats_track_tab; +DO $$ +BEGIN + DELETE FROM stats_track_tab; +END; $$; +DO LANGUAGE plpgsql $$ +BEGIN + -- this is a SELECT + PERFORM 'hello world'::TEXT; +END; $$; +SELECT toplevel, calls, query FROM pg_stat_monitor + ORDER BY query COLLATE "C", toplevel; + toplevel | calls | query +----------+-------+-------------------------------- + t | 2 | DELETE FROM stats_track_tab + t | 1 | SELECT $1::TEXT + t | 1 | SELECT pg_stat_monitor_reset() +(3 rows) + +-- PL/pgSQL function - top-level tracking. +SET pg_stat_monitor.pgsm_track = 'top'; +SET pg_stat_monitor.pgsm_track_utility = FALSE; +SELECT pg_stat_monitor_reset(); + pg_stat_monitor_reset +----------------------- + +(1 row) + +CREATE FUNCTION PLUS_TWO(i INTEGER) RETURNS INTEGER AS $$ +DECLARE + r INTEGER; +BEGIN + SELECT (i + 1 + 1.0)::INTEGER INTO r; + RETURN r; +END; $$ LANGUAGE plpgsql; +SELECT PLUS_TWO(3); + plus_two +---------- + 5 +(1 row) + +SELECT PLUS_TWO(7); + plus_two +---------- + 9 +(1 row) + +-- SQL function --- use LIMIT to keep it from being inlined +CREATE FUNCTION PLUS_ONE(i INTEGER) RETURNS INTEGER AS +$$ SELECT (i + 1.0)::INTEGER LIMIT 1 $$ LANGUAGE SQL; +SELECT PLUS_ONE(8); + plus_one +---------- + 9 +(1 row) + +SELECT PLUS_ONE(10); + plus_one +---------- + 11 +(1 row) + +SELECT calls, rows, query FROM pg_stat_monitor ORDER BY query COLLATE "C"; + calls | rows | query +-------+------+-------------------------------- + 2 | 2 | SELECT PLUS_ONE($1) + 2 | 2 | SELECT PLUS_TWO($1) + 1 | 1 | SELECT pg_stat_monitor_reset() +(3 rows) + +-- immutable SQL function --- can be executed at plan time +CREATE FUNCTION PLUS_THREE(i INTEGER) RETURNS INTEGER AS +$$ SELECT i + 3 LIMIT 1 $$ IMMUTABLE LANGUAGE SQL; +SELECT PLUS_THREE(8); + plus_three +------------ + 11 +(1 row) + +SELECT PLUS_THREE(10); + plus_three +------------ + 13 +(1 row) + +SELECT toplevel, calls, rows, query FROM pg_stat_monitor ORDER BY query COLLATE "C"; + toplevel | calls | rows | query +----------+-------+------+--------------------------------------------------------------------------- + t | 2 | 2 | SELECT PLUS_ONE($1) + t | 2 | 2 | SELECT PLUS_THREE($1) + t | 2 | 2 | SELECT PLUS_TWO($1) + t | 1 | 3 | SELECT calls, rows, query FROM pg_stat_monitor ORDER BY query COLLATE "C" + f | 2 | 2 | SELECT i + $2 LIMIT $3 + t | 1 | 1 | SELECT pg_stat_monitor_reset() +(6 rows) + +-- PL/pgSQL function - all-level tracking. +SET pg_stat_monitor.pgsm_track = 'all'; +SELECT pg_stat_monitor_reset(); + pg_stat_monitor_reset +----------------------- + +(1 row) + +-- we drop and recreate the functions to avoid any caching funnies +DROP FUNCTION PLUS_ONE(INTEGER); +DROP FUNCTION PLUS_TWO(INTEGER); +DROP FUNCTION PLUS_THREE(INTEGER); +-- PL/pgSQL function +CREATE FUNCTION PLUS_TWO(i INTEGER) RETURNS INTEGER AS $$ +DECLARE + r INTEGER; +BEGIN + SELECT (i + 1 + 1.0)::INTEGER INTO r; + RETURN r; +END; $$ LANGUAGE plpgsql; +SELECT PLUS_TWO(-1); + plus_two +---------- + 1 +(1 row) + +SELECT PLUS_TWO(2); + plus_two +---------- + 4 +(1 row) + +-- SQL function --- use LIMIT to keep it from being inlined +CREATE FUNCTION PLUS_ONE(i INTEGER) RETURNS INTEGER AS +$$ SELECT (i + 1.0)::INTEGER LIMIT 1 $$ LANGUAGE SQL; +SELECT PLUS_ONE(3); + plus_one +---------- + 4 +(1 row) + +SELECT PLUS_ONE(1); + plus_one +---------- + 2 +(1 row) + +SELECT calls, rows, query FROM pg_stat_monitor ORDER BY query COLLATE "C"; + calls | rows | query +-------+------+----------------------------------- + 2 | 2 | SELECT (i + $2 + $3)::INTEGER + 2 | 2 | SELECT (i + $2)::INTEGER LIMIT $3 + 2 | 2 | SELECT PLUS_ONE($1) + 2 | 2 | SELECT PLUS_TWO($1) + 1 | 1 | SELECT pg_stat_monitor_reset() +(5 rows) + +-- immutable SQL function --- can be executed at plan time +CREATE FUNCTION PLUS_THREE(i INTEGER) RETURNS INTEGER AS +$$ SELECT i + 3 LIMIT 1 $$ IMMUTABLE LANGUAGE SQL; +SELECT PLUS_THREE(8); + plus_three +------------ + 11 +(1 row) + +SELECT PLUS_THREE(10); + plus_three +------------ + 13 +(1 row) + +SELECT toplevel, calls, rows, query FROM pg_stat_monitor ORDER BY query COLLATE "C"; + toplevel | calls | rows | query +----------+-------+------+--------------------------------------------------------------------------- + f | 2 | 2 | SELECT (i + $2 + $3)::INTEGER + f | 2 | 2 | SELECT (i + $2)::INTEGER LIMIT $3 + t | 2 | 2 | SELECT PLUS_ONE($1) + t | 2 | 2 | SELECT PLUS_THREE($1) + t | 2 | 2 | SELECT PLUS_TWO($1) + t | 1 | 5 | SELECT calls, rows, query FROM pg_stat_monitor ORDER BY query COLLATE "C" + f | 2 | 2 | SELECT i + $2 LIMIT $3 + t | 1 | 1 | SELECT pg_stat_monitor_reset() +(8 rows) + +-- +-- pg_stat_monitor.pgsm_track = none +-- +SET pg_stat_monitor.pgsm_track = 'none'; +SELECT pg_stat_monitor_reset(); + pg_stat_monitor_reset +----------------------- + +(1 row) + +SELECT 1 AS "one"; + one +----- + 1 +(1 row) + +SELECT 1 + 1 AS "two"; + two +----- + 2 +(1 row) + +SELECT calls, rows, query FROM pg_stat_monitor ORDER BY query COLLATE "C"; + calls | rows | query +-------+------+------- +(0 rows) + +SELECT pg_stat_monitor_reset(); + pg_stat_monitor_reset +----------------------- + +(1 row) + +DROP EXTENSION pg_stat_monitor; diff --git a/regression/expected/level_tracking_2.out b/regression/expected/level_tracking_2.out new file mode 100644 index 00000000..9bda308f --- /dev/null +++ b/regression/expected/level_tracking_2.out @@ -0,0 +1,325 @@ +-- +-- Statement level tracking +-- +SELECT setting::integer < 140000 AS skip_test FROM pg_settings where name = 'server_version_num' \gset +\if :skip_test +\quit +\endif +CREATE EXTENSION pg_stat_monitor; +SET pg_stat_monitor.pgsm_track_utility = TRUE; +SET pg_stat_monitor.pgsm_normalized_query = TRUE; +SELECT pg_stat_monitor_reset(); + pg_stat_monitor_reset +----------------------- + +(1 row) + +-- DO block - top-level tracking. +CREATE TABLE stats_track_tab (x int); +SET pg_stat_monitor.pgsm_track = 'top'; +DELETE FROM stats_track_tab; +DO $$ +BEGIN + DELETE FROM stats_track_tab; +END; +$$ LANGUAGE plpgsql; +SELECT toplevel, calls, query FROM pg_stat_monitor + WHERE query LIKE '%DELETE%' ORDER BY query COLLATE "C", toplevel; + toplevel | calls | query +----------+-------+-------------------------------- + t | 1 | DELETE FROM stats_track_tab + t | 1 | DO $$ + + | | BEGIN + + | | DELETE FROM stats_track_tab;+ + | | END; + + | | $$ LANGUAGE plpgsql +(2 rows) + +SELECT pg_stat_monitor_reset(); + pg_stat_monitor_reset +----------------------- + +(1 row) + +-- DO block - all-level tracking. +SET pg_stat_monitor.pgsm_track = 'all'; +DELETE FROM stats_track_tab; +DO $$ +BEGIN + DELETE FROM stats_track_tab; +END; $$; +DO LANGUAGE plpgsql $$ +BEGIN + -- this is a SELECT + PERFORM 'hello world'::TEXT; +END; $$; +SELECT toplevel, calls, query FROM pg_stat_monitor + ORDER BY query COLLATE "C", toplevel; + toplevel | calls | query +----------+-------+---------------------------------------- + f | 1 | DELETE FROM stats_track_tab + t | 1 | DELETE FROM stats_track_tab + t | 1 | DO $$ + + | | BEGIN + + | | DELETE FROM stats_track_tab; + + | | END; $$ + t | 1 | DO LANGUAGE plpgsql $$ + + | | BEGIN + + | | -- this is a SELECT + + | | PERFORM 'hello world'::TEXT; + + | | END; $$ + f | 1 | SELECT $1::TEXT + t | 1 | SELECT pg_stat_monitor_reset() + t | 1 | SET pg_stat_monitor.pgsm_track = 'all' +(7 rows) + +-- DO block - top-level tracking without utility. +SET pg_stat_monitor.pgsm_track = 'top'; +SET pg_stat_monitor.pgsm_track_utility = FALSE; +SELECT pg_stat_monitor_reset(); + pg_stat_monitor_reset +----------------------- + +(1 row) + +DELETE FROM stats_track_tab; +DO $$ +BEGIN + DELETE FROM stats_track_tab; +END; $$; +DO LANGUAGE plpgsql $$ +BEGIN + -- this is a SELECT + PERFORM 'hello world'::TEXT; +END; $$; +SELECT toplevel, calls, query FROM pg_stat_monitor + ORDER BY query COLLATE "C", toplevel; + toplevel | calls | query +----------+-------+-------------------------------- + t | 1 | DELETE FROM stats_track_tab + t | 1 | SELECT pg_stat_monitor_reset() +(2 rows) + +-- DO block - all-level tracking without utility. +SET pg_stat_monitor.pgsm_track = 'all'; +SELECT pg_stat_monitor_reset(); + pg_stat_monitor_reset +----------------------- + +(1 row) + +DELETE FROM stats_track_tab; +DO $$ +BEGIN + DELETE FROM stats_track_tab; +END; $$; +DO LANGUAGE plpgsql $$ +BEGIN + -- this is a SELECT + PERFORM 'hello world'::TEXT; +END; $$; +SELECT toplevel, calls, query FROM pg_stat_monitor + ORDER BY query COLLATE "C", toplevel; + toplevel | calls | query +----------+-------+-------------------------------- + f | 1 | DELETE FROM stats_track_tab + t | 1 | DELETE FROM stats_track_tab + f | 1 | SELECT $1::TEXT + t | 1 | SELECT pg_stat_monitor_reset() +(4 rows) + +-- PL/pgSQL function - top-level tracking. +SET pg_stat_monitor.pgsm_track = 'top'; +SET pg_stat_monitor.pgsm_track_utility = FALSE; +SELECT pg_stat_monitor_reset(); + pg_stat_monitor_reset +----------------------- + +(1 row) + +CREATE FUNCTION PLUS_TWO(i INTEGER) RETURNS INTEGER AS $$ +DECLARE + r INTEGER; +BEGIN + SELECT (i + 1 + 1.0)::INTEGER INTO r; + RETURN r; +END; $$ LANGUAGE plpgsql; +SELECT PLUS_TWO(3); + plus_two +---------- + 5 +(1 row) + +SELECT PLUS_TWO(7); + plus_two +---------- + 9 +(1 row) + +-- SQL function --- use LIMIT to keep it from being inlined +CREATE FUNCTION PLUS_ONE(i INTEGER) RETURNS INTEGER AS +$$ SELECT (i + 1.0)::INTEGER LIMIT 1 $$ LANGUAGE SQL; +SELECT PLUS_ONE(8); + plus_one +---------- + 9 +(1 row) + +SELECT PLUS_ONE(10); + plus_one +---------- + 11 +(1 row) + +SELECT calls, rows, query FROM pg_stat_monitor ORDER BY query COLLATE "C"; + calls | rows | query +-------+------+-------------------------------- + 2 | 2 | SELECT PLUS_ONE($1) + 2 | 2 | SELECT PLUS_TWO($1) + 1 | 1 | SELECT pg_stat_monitor_reset() +(3 rows) + +-- immutable SQL function --- can be executed at plan time +CREATE FUNCTION PLUS_THREE(i INTEGER) RETURNS INTEGER AS +$$ SELECT i + 3 LIMIT 1 $$ IMMUTABLE LANGUAGE SQL; +SELECT PLUS_THREE(8); + plus_three +------------ + 11 +(1 row) + +SELECT PLUS_THREE(10); + plus_three +------------ + 13 +(1 row) + +SELECT toplevel, calls, rows, query FROM pg_stat_monitor ORDER BY query COLLATE "C"; + toplevel | calls | rows | query +----------+-------+------+--------------------------------------------------------------------------- + t | 2 | 2 | SELECT PLUS_ONE($1) + t | 2 | 2 | SELECT PLUS_THREE($1) + t | 2 | 2 | SELECT PLUS_TWO($1) + t | 1 | 3 | SELECT calls, rows, query FROM pg_stat_monitor ORDER BY query COLLATE "C" + t | 1 | 1 | SELECT pg_stat_monitor_reset() +(5 rows) + +-- PL/pgSQL function - all-level tracking. +SET pg_stat_monitor.pgsm_track = 'all'; +SELECT pg_stat_monitor_reset(); + pg_stat_monitor_reset +----------------------- + +(1 row) + +-- we drop and recreate the functions to avoid any caching funnies +DROP FUNCTION PLUS_ONE(INTEGER); +DROP FUNCTION PLUS_TWO(INTEGER); +DROP FUNCTION PLUS_THREE(INTEGER); +-- PL/pgSQL function +CREATE FUNCTION PLUS_TWO(i INTEGER) RETURNS INTEGER AS $$ +DECLARE + r INTEGER; +BEGIN + SELECT (i + 1 + 1.0)::INTEGER INTO r; + RETURN r; +END; $$ LANGUAGE plpgsql; +SELECT PLUS_TWO(-1); + plus_two +---------- + 1 +(1 row) + +SELECT PLUS_TWO(2); + plus_two +---------- + 4 +(1 row) + +-- SQL function --- use LIMIT to keep it from being inlined +CREATE FUNCTION PLUS_ONE(i INTEGER) RETURNS INTEGER AS +$$ SELECT (i + 1.0)::INTEGER LIMIT 1 $$ LANGUAGE SQL; +SELECT PLUS_ONE(3); + plus_one +---------- + 4 +(1 row) + +SELECT PLUS_ONE(1); + plus_one +---------- + 2 +(1 row) + +SELECT calls, rows, query FROM pg_stat_monitor ORDER BY query COLLATE "C"; + calls | rows | query +-------+------+----------------------------------- + 2 | 2 | SELECT (i + $2 + $3)::INTEGER + 2 | 2 | SELECT (i + $2)::INTEGER LIMIT $3 + 2 | 2 | SELECT PLUS_ONE($1) + 2 | 2 | SELECT PLUS_TWO($1) + 1 | 1 | SELECT pg_stat_monitor_reset() +(5 rows) + +-- immutable SQL function --- can be executed at plan time +CREATE FUNCTION PLUS_THREE(i INTEGER) RETURNS INTEGER AS +$$ SELECT i + 3 LIMIT 1 $$ IMMUTABLE LANGUAGE SQL; +SELECT PLUS_THREE(8); + plus_three +------------ + 11 +(1 row) + +SELECT PLUS_THREE(10); + plus_three +------------ + 13 +(1 row) + +SELECT toplevel, calls, rows, query FROM pg_stat_monitor ORDER BY query COLLATE "C"; + toplevel | calls | rows | query +----------+-------+------+--------------------------------------------------------------------------- + f | 2 | 2 | SELECT (i + $2 + $3)::INTEGER + f | 2 | 2 | SELECT (i + $2)::INTEGER LIMIT $3 + t | 2 | 2 | SELECT PLUS_ONE($1) + t | 2 | 2 | SELECT PLUS_THREE($1) + t | 2 | 2 | SELECT PLUS_TWO($1) + t | 1 | 5 | SELECT calls, rows, query FROM pg_stat_monitor ORDER BY query COLLATE "C" + f | 2 | 2 | SELECT i + $2 LIMIT $3 + t | 1 | 1 | SELECT pg_stat_monitor_reset() +(8 rows) + +-- +-- pg_stat_monitor.pgsm_track = none +-- +SET pg_stat_monitor.pgsm_track = 'none'; +SELECT pg_stat_monitor_reset(); + pg_stat_monitor_reset +----------------------- + +(1 row) + +SELECT 1 AS "one"; + one +----- + 1 +(1 row) + +SELECT 1 + 1 AS "two"; + two +----- + 2 +(1 row) + +SELECT calls, rows, query FROM pg_stat_monitor ORDER BY query COLLATE "C"; + calls | rows | query +-------+------+------- +(0 rows) + +SELECT pg_stat_monitor_reset(); + pg_stat_monitor_reset +----------------------- + +(1 row) + +DROP EXTENSION pg_stat_monitor; diff --git a/regression/expected/pgsm_query_id_1.out b/regression/expected/pgsm_query_id_1.out new file mode 100644 index 00000000..ecd519b5 --- /dev/null +++ b/regression/expected/pgsm_query_id_1.out @@ -0,0 +1,115 @@ +CREATE EXTENSION pg_stat_monitor; +CREATE DATABASE db1; +CREATE DATABASE db2; +\c db1 +CREATE TABLE t1 (a int); +CREATE TABLE t2 (b int); +CREATE FUNCTION add(integer, integer) RETURNS integer + AS 'select $1 + $2;' + LANGUAGE SQL + IMMUTABLE + RETURNS NULL ON NULL INPUT; +\c db2 +CREATE TABLE t1 (a int); +CREATE TABLE t3 (c int); +CREATE FUNCTION add(integer, integer) RETURNS integer + AS 'select $1 + $2;' + LANGUAGE SQL + IMMUTABLE + RETURNS NULL ON NULL INPUT; +\c contrib_regression +SELECT pg_stat_monitor_reset(); + pg_stat_monitor_reset +----------------------- + +(1 row) + +\c db1 +SELECT * FROM t1; + a +--- +(0 rows) + +SELECT *, ADD(1, 2) FROM t1; + a | add +---+----- +(0 rows) + +SELECT * FROM t2; + b +--- +(0 rows) + +-- Check that spaces and comments do not generate a different pgsm_query_id +SELECT * FROM t2 --WHATEVER; +; + b +--- +(0 rows) + +SELECT * FROM t2 /* ... +... +More comments to check for spaces. +*/ + ; + b +--- +(0 rows) + +\c db2 +SELECT * FROM t1; + a +--- +(0 rows) + +SELECT *, ADD(1, 2) FROM t1; + a | add +---+----- +(0 rows) + +set pg_stat_monitor.pgsm_enable_pgsm_query_id = off; +SELECT * FROM t3; + c +--- +(0 rows) + +set pg_stat_monitor.pgsm_enable_pgsm_query_id = on; +SELECT * FROM t3 where c = 20; + c +--- +(0 rows) + +\c contrib_regression +SELECT datname, pgsm_query_id, query, calls FROM pg_stat_monitor ORDER BY pgsm_query_id, query, datname; + datname | pgsm_query_id | query | calls +--------------------+---------------------+-----------------------------------------------------+------- + contrib_regression | 689150021118383254 | SELECT pg_stat_monitor_reset() | 1 + db1 | 1897482803466821995 | SELECT * FROM t2 | 3 + db1 | 1988437669671417938 | SELECT * FROM t1 | 1 + db2 | 1988437669671417938 | SELECT * FROM t1 | 1 + db2 | 6220142855706866455 | set pg_stat_monitor.pgsm_enable_pgsm_query_id = on | 1 + db2 | 6633979598391393345 | SELECT * FROM t3 where c = 20 | 1 + db1 | 8140395000078788481 | SELECT *, ADD(1, 2) FROM t1 | 1 + db2 | 8140395000078788481 | SELECT *, ADD(1, 2) FROM t1 | 1 + db2 | | SELECT * FROM t3 | 1 + db2 | | set pg_stat_monitor.pgsm_enable_pgsm_query_id = off | 1 +(10 rows) + +SELECT pg_stat_monitor_reset(); + pg_stat_monitor_reset +----------------------- + +(1 row) + +\c db1 +DROP TABLE t1; +DROP TABLE t2; +DROP FUNCTION ADD; +\c db2 +DROP TABLE t1; +DROP TABLE t3; +DROP FUNCTION ADD; +\c contrib_regression +DROP DATABASE db1; +DROP DATABASE db2; +DROP EXTENSION pg_stat_monitor; diff --git a/regression/expected/version.out b/regression/expected/version.out index 7ee08d7e..82cdec33 100644 --- a/regression/expected/version.out +++ b/regression/expected/version.out @@ -2,7 +2,7 @@ CREATE EXTENSION pg_stat_monitor; SELECT pg_stat_monitor_version(); pg_stat_monitor_version ------------------------- - 2.0.4 + 2.1.0 (1 row) DROP EXTENSION pg_stat_monitor; diff --git a/regression/sql/level_tracking.sql b/regression/sql/level_tracking.sql new file mode 100644 index 00000000..1ec891a1 --- /dev/null +++ b/regression/sql/level_tracking.sql @@ -0,0 +1,160 @@ +-- +-- Statement level tracking +-- + +SELECT setting::integer < 140000 AS skip_test FROM pg_settings where name = 'server_version_num' \gset +\if :skip_test +\quit +\endif + +CREATE EXTENSION pg_stat_monitor; +SET pg_stat_monitor.pgsm_track_utility = TRUE; +SET pg_stat_monitor.pgsm_normalized_query = TRUE; +SELECT pg_stat_monitor_reset(); + +-- DO block - top-level tracking. +CREATE TABLE stats_track_tab (x int); +SET pg_stat_monitor.pgsm_track = 'top'; +DELETE FROM stats_track_tab; +DO $$ +BEGIN + DELETE FROM stats_track_tab; +END; +$$ LANGUAGE plpgsql; +SELECT toplevel, calls, query FROM pg_stat_monitor + WHERE query LIKE '%DELETE%' ORDER BY query COLLATE "C", toplevel; +SELECT pg_stat_monitor_reset(); + +-- DO block - all-level tracking. +SET pg_stat_monitor.pgsm_track = 'all'; +DELETE FROM stats_track_tab; +DO $$ +BEGIN + DELETE FROM stats_track_tab; +END; $$; +DO LANGUAGE plpgsql $$ +BEGIN + -- this is a SELECT + PERFORM 'hello world'::TEXT; +END; $$; +SELECT toplevel, calls, query FROM pg_stat_monitor + ORDER BY query COLLATE "C", toplevel; + +-- DO block - top-level tracking without utility. +SET pg_stat_monitor.pgsm_track = 'top'; +SET pg_stat_monitor.pgsm_track_utility = FALSE; +SELECT pg_stat_monitor_reset(); +DELETE FROM stats_track_tab; +DO $$ +BEGIN + DELETE FROM stats_track_tab; +END; $$; +DO LANGUAGE plpgsql $$ +BEGIN + -- this is a SELECT + PERFORM 'hello world'::TEXT; +END; $$; +SELECT toplevel, calls, query FROM pg_stat_monitor + ORDER BY query COLLATE "C", toplevel; + +-- DO block - all-level tracking without utility. +SET pg_stat_monitor.pgsm_track = 'all'; +SELECT pg_stat_monitor_reset(); +DELETE FROM stats_track_tab; +DO $$ +BEGIN + DELETE FROM stats_track_tab; +END; $$; +DO LANGUAGE plpgsql $$ +BEGIN + -- this is a SELECT + PERFORM 'hello world'::TEXT; +END; $$; +SELECT toplevel, calls, query FROM pg_stat_monitor + ORDER BY query COLLATE "C", toplevel; + +-- PL/pgSQL function - top-level tracking. +SET pg_stat_monitor.pgsm_track = 'top'; +SET pg_stat_monitor.pgsm_track_utility = FALSE; +SELECT pg_stat_monitor_reset(); +CREATE FUNCTION PLUS_TWO(i INTEGER) RETURNS INTEGER AS $$ +DECLARE + r INTEGER; +BEGIN + SELECT (i + 1 + 1.0)::INTEGER INTO r; + RETURN r; +END; $$ LANGUAGE plpgsql; + +SELECT PLUS_TWO(3); +SELECT PLUS_TWO(7); + +-- SQL function --- use LIMIT to keep it from being inlined +CREATE FUNCTION PLUS_ONE(i INTEGER) RETURNS INTEGER AS +$$ SELECT (i + 1.0)::INTEGER LIMIT 1 $$ LANGUAGE SQL; + +SELECT PLUS_ONE(8); +SELECT PLUS_ONE(10); + +SELECT calls, rows, query FROM pg_stat_monitor ORDER BY query COLLATE "C"; + +-- immutable SQL function --- can be executed at plan time +CREATE FUNCTION PLUS_THREE(i INTEGER) RETURNS INTEGER AS +$$ SELECT i + 3 LIMIT 1 $$ IMMUTABLE LANGUAGE SQL; + +SELECT PLUS_THREE(8); +SELECT PLUS_THREE(10); + +SELECT toplevel, calls, rows, query FROM pg_stat_monitor ORDER BY query COLLATE "C"; + +-- PL/pgSQL function - all-level tracking. +SET pg_stat_monitor.pgsm_track = 'all'; +SELECT pg_stat_monitor_reset(); + +-- we drop and recreate the functions to avoid any caching funnies +DROP FUNCTION PLUS_ONE(INTEGER); +DROP FUNCTION PLUS_TWO(INTEGER); +DROP FUNCTION PLUS_THREE(INTEGER); + +-- PL/pgSQL function +CREATE FUNCTION PLUS_TWO(i INTEGER) RETURNS INTEGER AS $$ +DECLARE + r INTEGER; +BEGIN + SELECT (i + 1 + 1.0)::INTEGER INTO r; + RETURN r; +END; $$ LANGUAGE plpgsql; + +SELECT PLUS_TWO(-1); +SELECT PLUS_TWO(2); + +-- SQL function --- use LIMIT to keep it from being inlined +CREATE FUNCTION PLUS_ONE(i INTEGER) RETURNS INTEGER AS +$$ SELECT (i + 1.0)::INTEGER LIMIT 1 $$ LANGUAGE SQL; + +SELECT PLUS_ONE(3); +SELECT PLUS_ONE(1); + +SELECT calls, rows, query FROM pg_stat_monitor ORDER BY query COLLATE "C"; + +-- immutable SQL function --- can be executed at plan time +CREATE FUNCTION PLUS_THREE(i INTEGER) RETURNS INTEGER AS +$$ SELECT i + 3 LIMIT 1 $$ IMMUTABLE LANGUAGE SQL; + +SELECT PLUS_THREE(8); +SELECT PLUS_THREE(10); + +SELECT toplevel, calls, rows, query FROM pg_stat_monitor ORDER BY query COLLATE "C"; + +-- +-- pg_stat_monitor.pgsm_track = none +-- +SET pg_stat_monitor.pgsm_track = 'none'; +SELECT pg_stat_monitor_reset(); + +SELECT 1 AS "one"; +SELECT 1 + 1 AS "two"; + +SELECT calls, rows, query FROM pg_stat_monitor ORDER BY query COLLATE "C"; +SELECT pg_stat_monitor_reset(); + +DROP EXTENSION pg_stat_monitor; \ No newline at end of file diff --git a/t/018_column_names.pl b/t/018_column_names.pl index 6abe1860..80eb037a 100644 --- a/t/018_column_names.pl +++ b/t/018_column_names.pl @@ -22,7 +22,22 @@ close $conf; # Dictionary for expected PGSM columns names on different PG server versions -my %pg_versions_pgsm_columns = ( 16 => "application_name,blk_read_time," . +my %pg_versions_pgsm_columns = ( 17 => "application_name,". + "bucket,bucket_done,bucket_start_time,calls," . + "client_ip,cmd_type,cmd_type_text,comments,cpu_sys_time,cpu_user_time," . + "datname,dbid,elevel,jit_deform_count,jit_deform_time," . + "jit_emission_count,jit_emission_time,jit_functions,jit_generation_time," . + "jit_inlining_count,jit_inlining_time,jit_optimization_count,jit_optimization_time," . + "local_blk_read_time,local_blk_write_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,minmax_stats_since," . + "pgsm_query_id,planid,plans,query,query_plan,queryid,relations,resp_calls,rows," . + "shared_blk_read_time,shared_blk_write_time,shared_blks_dirtied," . + "shared_blks_hit,shared_blks_read,shared_blks_written,sqlcode,stats_since," . + "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,username,wal_bytes,wal_fpi,wal_records", +16 => "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,dbid,elevel,jit_emission_count,jit_emission_time,jit_functions," . diff --git a/t/025_compare_pgss.pl b/t/025_compare_pgss.pl index 05f940a0..5d1bffc6 100644 --- a/t/025_compare_pgss.pl +++ b/t/025_compare_pgss.pl @@ -35,6 +35,15 @@ my $rt_value = $node->start; ok($rt_value == 1, "Start Server"); +my $col_shared_blk_read_time = "shared_blk_read_time"; +my $col_shared_blk_write_time = "shared_blk_write_time"; + +if ($PGSM::PG_MAJOR_VERSION <= 16) +{ + $col_shared_blk_read_time = "blk_read_time"; + $col_shared_blk_write_time = "blk_write_time"; +} + # CREATE EXTENSION and change out file permissions my ($cmdret, $stdout, $stderr) = $node->psql('postgres', 'CREATE EXTENSION pg_stat_statements;', extra_params => ['-a']); ok($cmdret == 0, "CREATE PGSS EXTENSION"); @@ -78,10 +87,10 @@ ok($cmdret == 0, "Run pg_sleep for 2 seconds "); PGSM::append_to_file($stdout); -($cmdret, $stdout, $stderr) = $node->psql('postgres', 'SELECT substr(query,0,30),calls, rows, ROUND(total_exec_time::numeric,4) AS total_exec_time, ROUND(min_exec_time::numeric,4) AS min_exec_time, ROUND(max_exec_time::numeric,4) AS max_exec_time, ROUND(mean_exec_time::numeric,4) AS mean_exec_time, ROUND(stddev_exec_time::numeric,4) AS stddev_exec_time, ROUND(blk_read_time::numeric,4) AS blk_read_time, ROUND(blk_write_time::numeric,4) AS blk_write_time FROM pg_stat_statements WHERE query LIKE \'%bench%\' ORDER BY query,calls DESC;', extra_params => ['-a', '-Pformat=aligned','-Ptuples_only=off']); +($cmdret, $stdout, $stderr) = $node->psql('postgres', "SELECT substr(query,0,30),calls, rows, ROUND(total_exec_time::numeric,4) AS total_exec_time, ROUND(min_exec_time::numeric,4) AS min_exec_time, ROUND(max_exec_time::numeric,4) AS max_exec_time, ROUND(mean_exec_time::numeric,4) AS mean_exec_time, ROUND(stddev_exec_time::numeric,4) AS stddev_exec_time, ROUND(${col_shared_blk_read_time}::numeric,4) AS ${col_shared_blk_read_time}, ROUND(${col_shared_blk_write_time}::numeric,4) AS ${col_shared_blk_write_time} FROM pg_stat_statements WHERE query LIKE '%bench%' ORDER BY query,calls DESC;", extra_params => ['-a', '-Pformat=aligned','-Ptuples_only=off']); PGSM::append_to_debug_file($stdout); -($cmdret, $stdout, $stderr) = $node->psql('postgres', 'SELECT bucket, bucket_start_time, queryid, substr(query,0,30) AS query, calls, rows, total_exec_time, min_exec_time, max_exec_time, mean_exec_time, stddev_exec_time, ROUND(blk_read_time::numeric,4) AS blk_read_time, ROUND(blk_write_time::numeric,4) AS blk_write_time, cpu_user_time, cpu_sys_time FROM pg_stat_monitor WHERE query LIKE \'%bench%\' ORDER BY query,calls DESC;', extra_params => ['-a', '-Pformat=aligned','-Ptuples_only=off']); +($cmdret, $stdout, $stderr) = $node->psql('postgres', "SELECT bucket, bucket_start_time, queryid, substr(query,0,30) AS query, calls, rows, total_exec_time, min_exec_time, max_exec_time, mean_exec_time, stddev_exec_time, ROUND(${col_shared_blk_read_time}::numeric,4) AS ${col_shared_blk_read_time}, ROUND(${col_shared_blk_write_time}::numeric,4) AS ${col_shared_blk_write_time}, cpu_user_time, cpu_sys_time FROM pg_stat_monitor WHERE query LIKE '%bench%' ORDER BY query,calls DESC;", extra_params => ['-a', '-Pformat=aligned','-Ptuples_only=off']); PGSM::append_to_debug_file($stdout); ($cmdret, $stdout, $stderr) = $node->psql('postgres', 'SELECT substr(query,0,30) AS query,calls,rows,wal_records,wal_fpi,wal_bytes FROM pg_stat_statements WHERE query LIKE \'%bench%\' ORDER BY query,calls;', extra_params => ['-a', '-Pformat=aligned','-Ptuples_only=off']); @@ -160,13 +169,13 @@ trim($stdout); is($stdout,'t',"Compare: stddev_exec_time is equal."); -($cmdret, $stdout, $stderr) = $node->psql('postgres', 'SELECT SUM(ROUND(PGSM.blk_read_time::numeric,4)) = SUM(ROUND(PGSS.blk_read_time::numeric,4)) FROM pg_stat_monitor AS PGSM INNER JOIN pg_stat_statements AS PGSS ON PGSS.query = PGSM.query WHERE PGSM.query LIKE \'%INSERT INTO pgbench_history%\' GROUP BY PGSM.query;', extra_params => ['-Pformat=unaligned','-Ptuples_only=on']); +($cmdret, $stdout, $stderr) = $node->psql('postgres', "SELECT SUM(ROUND(PGSM.${col_shared_blk_read_time}::numeric,4)) = SUM(ROUND(PGSS.${col_shared_blk_read_time}::numeric,4)) FROM pg_stat_monitor AS PGSM INNER JOIN pg_stat_statements AS PGSS ON PGSS.query = PGSM.query WHERE PGSM.query LIKE \'%INSERT INTO pgbench_history%\' GROUP BY PGSM.query;", extra_params => ['-Pformat=unaligned','-Ptuples_only=on']); trim($stdout); -is($stdout,'t',"Compare: blk_read_time is equal."); +is($stdout,'t',"Compare: ${col_shared_blk_read_time} is equal."); -($cmdret, $stdout, $stderr) = $node->psql('postgres', 'SELECT SUM(ROUND(PGSM.blk_write_time::numeric,4)) = SUM(ROUND(PGSS.blk_write_time::numeric,4)) FROM pg_stat_monitor AS PGSM INNER JOIN pg_stat_statements AS PGSS ON PGSS.query = PGSM.query WHERE PGSM.query LIKE \'%INSERT INTO pgbench_history%\' GROUP BY PGSM.query;', extra_params => ['-Pformat=unaligned','-Ptuples_only=on']); +($cmdret, $stdout, $stderr) = $node->psql('postgres', "SELECT SUM(ROUND(PGSM.${col_shared_blk_write_time}::numeric,4)) = SUM(ROUND(PGSS.${col_shared_blk_write_time}::numeric,4)) FROM pg_stat_monitor AS PGSM INNER JOIN pg_stat_statements AS PGSS ON PGSS.query = PGSM.query WHERE PGSM.query LIKE \'%INSERT INTO pgbench_history%\' GROUP BY PGSM.query;", extra_params => ['-Pformat=unaligned','-Ptuples_only=on']); trim($stdout); -is($stdout,'t',"Compare: blk_write_time is equal."); +is($stdout,'t',"Compare: ${col_shared_blk_write_time} is equal."); ($cmdret, $stdout, $stderr) = $node->psql('postgres', 'SELECT SUM(PGSM.wal_records) = SUM(PGSS.wal_records) FROM pg_stat_monitor AS PGSM INNER JOIN pg_stat_statements AS PGSS ON PGSS.query = PGSM.query WHERE PGSM.query LIKE \'%INSERT INTO pgbench_history%\' GROUP BY PGSM.query;', extra_params => ['-Pformat=unaligned','-Ptuples_only=on']); trim($stdout); @@ -209,13 +218,13 @@ trim($stdout); is($stdout,'t',"Compare: stddev_exec_time is equal."); -($cmdret, $stdout, $stderr) = $node->psql('postgres', 'SELECT SUM(ROUND(PGSM.blk_read_time::numeric,4)) = SUM(ROUND(PGSS.blk_read_time::numeric,4)) FROM pg_stat_monitor AS PGSM INNER JOIN pg_stat_statements AS PGSS ON PGSS.query = PGSM.query WHERE PGSM.query LIKE \'%SELECT abalance FROM pgbench_accounts%\' GROUP BY PGSM.query;', extra_params => ['-Pformat=unaligned','-Ptuples_only=on']); +($cmdret, $stdout, $stderr) = $node->psql('postgres', "SELECT SUM(ROUND(PGSM.${col_shared_blk_read_time}::numeric,4)) = SUM(ROUND(PGSS.${col_shared_blk_read_time}::numeric,4)) FROM pg_stat_monitor AS PGSM INNER JOIN pg_stat_statements AS PGSS ON PGSS.query = PGSM.query WHERE PGSM.query LIKE \'%SELECT abalance FROM pgbench_accounts%\' GROUP BY PGSM.query;", extra_params => ['-Pformat=unaligned','-Ptuples_only=on']); trim($stdout); -is($stdout,'t',"Compare: blk_read_time is equal."); +is($stdout,'t',"Compare: ${col_shared_blk_read_time} is equal."); -($cmdret, $stdout, $stderr) = $node->psql('postgres', 'SELECT SUM(ROUND(PGSM.blk_write_time::numeric,4)) = SUM(ROUND(PGSS.blk_write_time::numeric,4)) FROM pg_stat_monitor AS PGSM INNER JOIN pg_stat_statements AS PGSS ON PGSS.query = PGSM.query WHERE PGSM.query LIKE \'%SELECT abalance FROM pgbench_accounts%\' GROUP BY PGSM.query;', extra_params => ['-Pformat=unaligned','-Ptuples_only=on']); +($cmdret, $stdout, $stderr) = $node->psql('postgres', "SELECT SUM(ROUND(PGSM.${col_shared_blk_write_time}::numeric,4)) = SUM(ROUND(PGSS.${col_shared_blk_write_time}::numeric,4)) FROM pg_stat_monitor AS PGSM INNER JOIN pg_stat_statements AS PGSS ON PGSS.query = PGSM.query WHERE PGSM.query LIKE \'%SELECT abalance FROM pgbench_accounts%\' GROUP BY PGSM.query;", extra_params => ['-Pformat=unaligned','-Ptuples_only=on']); trim($stdout); -is($stdout,'t',"Compare: blk_write_time is equal."); +is($stdout,'t',"Compare: ${col_shared_blk_write_time} is equal."); ($cmdret, $stdout, $stderr) = $node->psql('postgres', 'SELECT SUM(PGSM.wal_records) = SUM(PGSS.wal_records) FROM pg_stat_monitor AS PGSM INNER JOIN pg_stat_statements AS PGSS ON PGSS.query = PGSM.query WHERE PGSM.query LIKE \'%SELECT abalance FROM pgbench_accounts%\' GROUP BY PGSM.query;', extra_params => ['-Pformat=unaligned','-Ptuples_only=on']); trim($stdout); @@ -258,9 +267,9 @@ trim($stdout); is($stdout,'t',"Compare: stddev_exec_time is equal."); -($cmdret, $stdout, $stderr) = $node->psql('postgres', 'SELECT SUM(ROUND(PGSM.blk_write_time::numeric,4)) = SUM(ROUND(PGSS.blk_write_time::numeric,4)) FROM pg_stat_monitor AS PGSM INNER JOIN pg_stat_statements AS PGSS ON PGSS.query = PGSM.query WHERE PGSM.query LIKE \'%UPDATE pgbench_accounts%\' GROUP BY PGSM.query;', extra_params => ['-Pformat=unaligned','-Ptuples_only=on']); +($cmdret, $stdout, $stderr) = $node->psql('postgres', "SELECT SUM(ROUND(PGSM.${col_shared_blk_write_time}::numeric,4)) = SUM(ROUND(PGSS.${col_shared_blk_write_time}::numeric,4)) FROM pg_stat_monitor AS PGSM INNER JOIN pg_stat_statements AS PGSS ON PGSS.query = PGSM.query WHERE PGSM.query LIKE \'%UPDATE pgbench_accounts%\' GROUP BY PGSM.query;", extra_params => ['-Pformat=unaligned','-Ptuples_only=on']); trim($stdout); -is($stdout,'t',"Compare: blk_write_time is equal."); +is($stdout,'t',"Compare: ${col_shared_blk_write_time} is equal."); ($cmdret, $stdout, $stderr) = $node->psql('postgres', 'SELECT SUM(PGSM.wal_records) = SUM(PGSS.wal_records) FROM pg_stat_monitor AS PGSM INNER JOIN pg_stat_statements AS PGSS ON PGSS.query = PGSM.query WHERE PGSM.query LIKE \'%UPDATE pgbench_accounts%\' GROUP BY PGSM.query;', extra_params => ['-Pformat=unaligned','-Ptuples_only=on']); trim($stdout); diff --git a/t/026_shared_blocks.pl b/t/026_shared_blocks.pl index 94b90a44..7b02a9e8 100644 --- a/t/026_shared_blocks.pl +++ b/t/026_shared_blocks.pl @@ -35,6 +35,15 @@ my $rt_value = $node->start; ok($rt_value == 1, "Start Server"); +my $col_shared_blk_read_time = "shared_blk_read_time"; +my $col_shared_blk_write_time = "shared_blk_write_time"; + +if ($PGSM::PG_MAJOR_VERSION <= 16) +{ + $col_shared_blk_read_time = "blk_read_time"; + $col_shared_blk_write_time = "blk_write_time"; +} + # CREATE EXTENSION and change out file permissions my ($cmdret, $stdout, $stderr) = $node->psql('postgres', 'CREATE EXTENSION pg_stat_statements;', extra_params => ['-a']); ok($cmdret == 0, "CREATE PGSS EXTENSION"); @@ -86,7 +95,7 @@ PGSM::append_to_debug_file($stdout); PGSM::append_to_debug_file("--------"); -($cmdret, $stdout, $stderr) = $node->psql('postgres', 'SELECT substr(query,0,130) AS query, calls, rows, shared_blks_hit, shared_blks_read, shared_blks_dirtied, shared_blks_written, blk_read_time, blk_write_time FROM pg_stat_monitor WHERE query LIKE \'%bench%\' ORDER BY query,calls DESC;', extra_params => ['-a', '-Pformat=aligned','-Ptuples_only=off']); +($cmdret, $stdout, $stderr) = $node->psql('postgres', "SELECT substr(query,0,130) AS query, calls, rows, shared_blks_hit, shared_blks_read, shared_blks_dirtied, shared_blks_written, ${col_shared_blk_read_time}, ${col_shared_blk_write_time} FROM pg_stat_monitor WHERE query LIKE '%bench%' ORDER BY query,calls DESC;", extra_params => ['-a', '-Pformat=aligned','-Ptuples_only=off']); PGSM::append_to_debug_file($stdout); # Compare values for query 'DELETE FROM pgbench_accounts WHERE $1 = $2' @@ -106,13 +115,13 @@ trim($stdout); is($stdout,'t',"Check: shared_blks_written should not be 0."); -($cmdret, $stdout, $stderr) = $node->psql('postgres','SELECT SUM(PGSM.blk_read_time) != 0 FROM pg_stat_monitor AS PGSM WHERE PGSM.query LIKE \'%DELETE FROM pgbench_accounts%\' GROUP BY PGSM.query;', extra_params => ['-Pformat=unaligned','-Ptuples_only=on']); +($cmdret, $stdout, $stderr) = $node->psql('postgres',"SELECT SUM(PGSM.${col_shared_blk_read_time}) != 0 FROM pg_stat_monitor AS PGSM WHERE PGSM.query LIKE '%DELETE FROM pgbench_accounts%' GROUP BY PGSM.query;", extra_params => ['-Pformat=unaligned','-Ptuples_only=on']); trim($stdout); -is($stdout,'t',"Check: blk_read_time should not be 0."); +is($stdout,'t',"Check: ${col_shared_blk_read_time} should not be 0."); -($cmdret, $stdout, $stderr) = $node->psql('postgres','SELECT SUM(PGSM.blk_write_time) != 0 FROM pg_stat_monitor AS PGSM WHERE PGSM.query LIKE \'%DELETE FROM pgbench_accounts%\' GROUP BY PGSM.query;', extra_params => ['-Pformat=unaligned','-Ptuples_only=on']); +($cmdret, $stdout, $stderr) = $node->psql('postgres',"SELECT SUM(PGSM.${col_shared_blk_write_time}) != 0 FROM pg_stat_monitor AS PGSM WHERE PGSM.query LIKE '%DELETE FROM pgbench_accounts%' GROUP BY PGSM.query;", extra_params => ['-Pformat=unaligned','-Ptuples_only=on']); trim($stdout); -is($stdout,'t',"Check: blk_write_time should not be 0."); +is($stdout,'t',"Check: ${col_shared_blk_write_time} should not be 0."); # Compare values for query 'INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES ($1, $2, $3, $4, CURRENT_TIMESTAMP)' ($cmdret, $stdout, $stderr) = $node->psql('postgres','SELECT SUM(PGSM.shared_blks_hit) != 0 FROM pg_stat_monitor AS PGSM WHERE PGSM.query LIKE \'%INSERT INTO pgbench_history%\' GROUP BY PGSM.query;', extra_params => ['-Pformat=unaligned','-Ptuples_only=on']); @@ -127,9 +136,9 @@ trim($stdout); is($stdout,'t',"Check: shared_blks_written should not be 0."); -($cmdret, $stdout, $stderr) = $node->psql('postgres','SELECT SUM(PGSM.blk_write_time) != 0 FROM pg_stat_monitor AS PGSM WHERE PGSM.query LIKE \'%INSERT INTO pgbench_history%\' GROUP BY PGSM.query;', extra_params => ['-Pformat=unaligned','-Ptuples_only=on']); +($cmdret, $stdout, $stderr) = $node->psql('postgres',"SELECT SUM(PGSM.${col_shared_blk_write_time}) != 0 FROM pg_stat_monitor AS PGSM WHERE PGSM.query LIKE '%INSERT INTO pgbench_history%' GROUP BY PGSM.query;", extra_params => ['-Pformat=unaligned','-Ptuples_only=on']); trim($stdout); -is($stdout,'t',"Check: blk_write_time should not be 0."); +is($stdout,'t',"Check: ${col_shared_blk_write_time} should not be 0."); # Compare values for query 'UPDATE pgbench_accounts SET abalance = abalance + $1 WHERE aid = $2' ($cmdret, $stdout, $stderr) = $node->psql('postgres','SELECT SUM(PGSM.shared_blks_hit) != 0 FROM pg_stat_monitor AS PGSM WHERE PGSM.query LIKE \'%UPDATE pgbench_accounts SET abalance%\' GROUP BY PGSM.query;', extra_params => ['-Pformat=unaligned','-Ptuples_only=on']); @@ -148,13 +157,13 @@ trim($stdout); is($stdout,'t',"Check: shared_blks_written should not be 0."); -($cmdret, $stdout, $stderr) = $node->psql('postgres','SELECT SUM(PGSM.blk_read_time) != 0 FROM pg_stat_monitor AS PGSM WHERE PGSM.query LIKE \'%UPDATE pgbench_accounts SET abalance%\' GROUP BY PGSM.query;', extra_params => ['-Pformat=unaligned','-Ptuples_only=on']); +($cmdret, $stdout, $stderr) = $node->psql('postgres', "SELECT SUM(PGSM.${col_shared_blk_read_time}) != 0 FROM pg_stat_monitor AS PGSM WHERE PGSM.query LIKE '%UPDATE pgbench_accounts SET abalance%' GROUP BY PGSM.query;", extra_params => ['-Pformat=unaligned','-Ptuples_only=on']); trim($stdout); -is($stdout,'t',"Check: blk_read_time should not be 0."); +is($stdout,'t',"Check: ${col_shared_blk_read_time} should not be 0."); -($cmdret, $stdout, $stderr) = $node->psql('postgres','SELECT SUM(PGSM.blk_write_time) != 0 FROM pg_stat_monitor AS PGSM WHERE PGSM.query LIKE \'%UPDATE pgbench_accounts SET abalance%\' GROUP BY PGSM.query;', extra_params => ['-Pformat=unaligned','-Ptuples_only=on']); +($cmdret, $stdout, $stderr) = $node->psql('postgres',"SELECT SUM(PGSM.${col_shared_blk_write_time}) != 0 FROM pg_stat_monitor AS PGSM WHERE PGSM.query LIKE '%UPDATE pgbench_accounts SET abalance%' GROUP BY PGSM.query;", extra_params => ['-Pformat=unaligned','-Ptuples_only=on']); trim($stdout); -is($stdout,'t',"Check: blk_write_time should not be 0."); +is($stdout,'t',"Check: ${col_shared_blk_write_time} should not be 0."); # DROP EXTENSION $stdout = $node->safe_psql('postgres', 'DROP EXTENSION pg_stat_monitor;', extra_params => ['-a']); diff --git a/t/027_local_blocks.pl b/t/027_local_blocks.pl index f0c628ba..c1ff01d6 100644 --- a/t/027_local_blocks.pl +++ b/t/027_local_blocks.pl @@ -70,10 +70,26 @@ trim($stdout); is($stdout,'t',"Check: local_blks_dirtied should not be 0."); +if ($PGSM::PG_MAJOR_VERSION >= 17) +{ + ($cmdret, $stdout, $stderr) = $node->psql('postgres', 'SELECT SUM(PGSM.local_blk_write_time) != 0 FROM pg_stat_monitor AS PGSM WHERE PGSM.query LIKE \'%INSERT INTO t1%\'', extra_params => ['-Pformat=unaligned','-Ptuples_only=on']); + trim($stdout); + is($stdout,'t',"Check: local_blk_write_time should not be 0."); +} + + # Compare values for query 'SELECT * FROM t1' ($cmdret, $stdout, $stderr) = $node->psql('postgres', 'SELECT PGSM.local_blks_hit != 0 FROM pg_stat_monitor AS PGSM WHERE PGSM.query LIKE \'%FROM t1%\';', extra_params => ['-Pformat=unaligned','-Ptuples_only=on']); trim($stdout); -is($stdout,'t',"Check: shared_blks_hit should not be 0."); +is($stdout,'t',"Check: local_blks_hit should not be 0."); + +# TODO: Find a way how to bypass cache and get real block reads +# if ($PGSM::PG_MAJOR_VERSION >= 17) +# { +# ($cmdret, $stdout, $stderr) = $node->psql('postgres', 'SELECT SUM(PGSM.local_blk_read_time) != 0 FROM pg_stat_monitor AS PGSM WHERE PGSM.query LIKE \'%FROM t1%\';', extra_params => ['-Pformat=unaligned','-Ptuples_only=on']); +# trim($stdout); +# is($stdout,'t',"Check: local_blk_read_time should not be 0."); +# } # DROP EXTENSION $stdout = $node->safe_psql('postgres', 'DROP EXTENSION pg_stat_monitor;', extra_params => ['-a']); diff --git a/t/028_temp_block.pl b/t/028_temp_block.pl index 27db3ceb..567fc68f 100644 --- a/t/028_temp_block.pl +++ b/t/028_temp_block.pl @@ -85,11 +85,22 @@ # Compare values for query 'SELECT * FROM t1' ($cmdret, $stdout, $stderr) = $node->psql('postgres', 'SELECT PGSM.temp_blks_read != 0 FROM pg_stat_monitor AS PGSM WHERE PGSM.query LIKE \'%FROM t1%\';', extra_params => ['-Pformat=unaligned','-Ptuples_only=on']); trim($stdout); -is($stdout,'t',"Check: shared_blks_hit should not be 0."); +is($stdout,'t',"Check: temp_blks_read should not be 0."); ($cmdret, $stdout, $stderr) = $node->psql('postgres', 'SELECT PGSM.temp_blks_written != 0 FROM pg_stat_monitor AS PGSM WHERE PGSM.query LIKE \'%FROM t1%\';', extra_params => ['-Pformat=unaligned','-Ptuples_only=on']); trim($stdout); -is($stdout,'t',"Check: temp_blks_read should not be 0."); +is($stdout,'t',"Check: temp_blks_written should not be 0."); + +if ($PGSM::PG_MAJOR_VERSION >= 15) +{ + ($cmdret, $stdout, $stderr) = $node->psql('postgres', 'SELECT SUM(PGSM.temp_blk_read_time) != 0 FROM pg_stat_monitor AS PGSM WHERE PGSM.query LIKE \'%FROM t1%\';', extra_params => ['-Pformat=unaligned','-Ptuples_only=on']); + trim($stdout); + is($stdout,'t',"Check: temp_blk_read_time should not be 0."); + + ($cmdret, $stdout, $stderr) = $node->psql('postgres', 'SELECT SUM(PGSM.temp_blk_write_time) != 0 FROM pg_stat_monitor AS PGSM WHERE PGSM.query LIKE \'%FROM t1%\'', extra_params => ['-Pformat=unaligned','-Ptuples_only=on']); + trim($stdout); + is($stdout,'t',"Check: temp_blk_write_time should not be 0."); +} # DROP EXTENSION $stdout = $node->safe_psql('postgres', 'DROP EXTENSION pg_stat_monitor;', extra_params => ['-a']); diff --git a/t/033_stats_since.pl b/t/033_stats_since.pl new file mode 100644 index 00000000..e151886b --- /dev/null +++ b/t/033_stats_since.pl @@ -0,0 +1,89 @@ +#!/usr/bin/perl + +use strict; +use warnings; +use File::Basename; +use File::Compare; +use File::Copy; +use Text::Trim 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)); + +if ($PGSM::PG_MAJOR_VERSION <= 16) +{ + plan skip_all => "pg_stat_monitor test cases for versions 16 and below."; +} + +# 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 +$node->append_conf('postgresql.conf', "shared_preload_libraries = 'pg_stat_monitor'"); + +# Set change postgresql.conf for this test case. +$node->append_conf('postgresql.conf', "pg_stat_monitor.pgsm_bucket_time = 1"); +$node->append_conf('postgresql.conf', "pg_stat_monitor.pgsm_max_buckets = 3"); +$node->append_conf('postgresql.conf', "pg_stat_monitor.pgsm_normalized_query = yes"); +$node->append_conf('postgresql.conf', "pg_stat_monitor.pgsm_track = 'all'"); + +# 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_debug_file($stdout); + +# Run 'SELECT pg_stat_monitor settings' and dump output to out file +($cmdret, $stdout, $stderr) = $node->psql('postgres', "SELECT name, setting, unit, context, vartype, source, min_val, max_val, enumvals, boot_val, reset_val, pending_restart FROM pg_settings WHERE name LIKE '%pg_stat_monitor%';", extra_params => ['-a', '-Pformat=aligned','-Ptuples_only=off']); +ok($cmdret == 0, "Print PGSM EXTENSION Settings"); +PGSM::append_to_debug_file($stdout); + +# Run required commands/queries and dump output to out file. +($cmdret, $stdout, $stderr) = $node->psql('postgres', 'SELECT pg_stat_monitor_reset();', extra_params => ['-a', '-Pformat=aligned','-Ptuples_only=off']); +ok($cmdret == 0, "Reset PGSM EXTENSION"); +PGSM::append_to_debug_file($stdout); + +($cmdret, $stdout, $stderr) = $node->psql('postgres', 'SELECT pg_sleep(1);', extra_params => ['-a', '-Pformat=aligned','-Ptuples_only=off']); +ok($cmdret == 0, "1 - Run pg_sleep(1)"); +PGSM::append_to_debug_file($stdout); + +($cmdret, $stdout, $stderr) = $node->psql('postgres', 'SELECT pg_sleep(1);', extra_params => ['-a', '-Pformat=aligned','-Ptuples_only=off']); +ok($cmdret == 0, "2 - Run pg_sleep(1)"); +PGSM::append_to_debug_file($stdout); + +($cmdret, $stdout, $stderr) = $node->psql('postgres', 'SELECT pg_sleep(1);', extra_params => ['-a', '-Pformat=aligned','-Ptuples_only=off']); +ok($cmdret == 0, "3 - Run pg_sleep(1)"); +PGSM::append_to_debug_file($stdout); + +# Check that we have more than one bucket +($cmdret, $stdout, $stderr) = $node->psql('postgres', "SELECT COUNT(bucket) != 0 AS PGSM FROM pg_stat_monitor WHERE query LIKE '%sleep%';", extra_params => ['-t', '-Pformat=unaligned','-Ptuples_only=on']); +trim($stdout); +is($stdout,'t',"Check: we have more that one bucket"); + +# Check that stats timestamps are different for each query/bucket +($cmdret, $stdout, $stderr) = $node->psql('postgres', "SELECT COUNT(DISTINCT stats_since) = COUNT(stats_since) AS PGSM FROM pg_stat_monitor WHERE query LIKE '%sleep%';", extra_params => ['-t', '-Pformat=unaligned','-Ptuples_only=on']); +trim($stdout); +is($stdout,'t',"Check: for every bucket stats_since should be unique"); + +# Check that minmax_stats_since always match stats_since +($cmdret, $stdout, $stderr) = $node->psql('postgres', "SELECT COUNT(*) AS ST FROM pg_stat_monitor WHERE query LIKE '%sleep%' AND stats_since != minmax_stats_since;", extra_params => ['-a', '-Pformat=aligned','-Ptuples_only=off']); +ok($stdout == 0, "Compare: Calls count is 1"); +PGSM::append_to_debug_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_debug_file($stdout); + +# Stop the server +$node->stop; + +# Done testing for this testcase file. +done_testing(); diff --git a/t/expected/007_settings_pgsm_query_shared_buffer.out.17 b/t/expected/007_settings_pgsm_query_shared_buffer.out.17 new file mode 100644 index 00000000..72c1e9a2 --- /dev/null +++ b/t/expected/007_settings_pgsm_query_shared_buffer.out.17 @@ -0,0 +1,151 @@ +CREATE EXTENSION pg_stat_monitor; +SELECT pg_stat_monitor_reset(); + pg_stat_monitor_reset +----------------------- + +(1 row) + +SELECT name, setting, unit, context, vartype, source, min_val, max_val, enumvals, boot_val, reset_val, pending_restart FROM pg_settings WHERE name='pg_stat_monitor.pgsm_query_shared_buffer'; + name | setting | unit | context | vartype | source | min_val | max_val | enumvals | boot_val | reset_val | pending_restart +------------------------------------------+---------+------+------------+---------+--------------------+---------+---------+----------+----------+-----------+----------------- + pg_stat_monitor.pgsm_query_shared_buffer | 1 | MB | postmaster | integer | configuration file | 1 | 10000 | | 20 | 1 | f +(1 row) + +CREATE database example; +SELECT datname, substr(query,0,150) AS query, SUM(calls) AS calls FROM pg_stat_monitor GROUP BY datname, query ORDER BY datname, query, calls DESC Limit 20; + datname | query | calls +---------+-------------------------------------------------------------------------------------------------------------------------------------------------------+------- + example | INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES ($1, $2, $3, $4, CURRENT_TIMESTAMP) | 10000 + example | SELECT abalance FROM pgbench_accounts WHERE aid = $1 | 10000 + example | UPDATE pgbench_accounts SET abalance = abalance + $1 WHERE aid = $2 | 10000 + example | UPDATE pgbench_branches SET bbalance = bbalance + $1 WHERE bid = $2 | 10000 + example | UPDATE pgbench_tellers SET tbalance = tbalance + $1 WHERE tid = $2 | 10000 + example | alter table pgbench_accounts add primary key (aid) | 1 + example | alter table pgbench_branches add primary key (bid) | 1 + example | alter table pgbench_tellers add primary key (tid) | 1 + example | begin | 10001 + example | commit | 10001 + example | copy pgbench_accounts from stdin with (freeze on) | 1 + example | copy pgbench_branches from stdin with (freeze on) | 1 + example | copy pgbench_tellers from stdin with (freeze on) | 1 + example | create table pgbench_accounts(aid int not null,bid int,abalance int,filler char(84)) with (fillfactor=100) | 1 + example | create table pgbench_branches(bid int not null,bbalance int,filler char(88)) with (fillfactor=100) | 1 + example | create table pgbench_history(tid int,bid int,aid int,delta int,mtime timestamp,filler char(22)) | 1 + example | create table pgbench_tellers(tid int not null,bid int,tbalance int,filler char(84)) with (fillfactor=100) | 1 + example | drop table if exists pgbench_accounts, pgbench_branches, pgbench_history, pgbench_tellers | 1 + example | select count(*) from pgbench_branches | 1 + example | select o.n, p.partstrat, pg_catalog.count(i.inhparent) from pg_catalog.pg_class as c join pg_catalog.pg_namespace as n on (n.oid = c.relnamespace) cr | 1 +(20 rows) + +SELECT pg_stat_monitor_reset(); + pg_stat_monitor_reset +----------------------- + +(1 row) + +SELECT name, setting, unit, context, vartype, source, min_val, max_val, enumvals, boot_val, reset_val, pending_restart FROM pg_settings WHERE name='pg_stat_monitor.pgsm_query_shared_buffer'; + name | setting | unit | context | vartype | source | min_val | max_val | enumvals | boot_val | reset_val | pending_restart +------------------------------------------+---------+------+------------+---------+--------------------+---------+---------+----------+----------+-----------+----------------- + pg_stat_monitor.pgsm_query_shared_buffer | 2 | MB | postmaster | integer | configuration file | 1 | 10000 | | 20 | 2 | f +(1 row) + +SELECT datname, substr(query,0,150) AS query, SUM(calls) AS calls FROM pg_stat_monitor GROUP BY datname, query ORDER BY datname, query, calls DESC Limit 20; + datname | query | calls +---------+-------------------------------------------------------------------------------------------------------------------------------------------------------+------- + example | INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES ($1, $2, $3, $4, CURRENT_TIMESTAMP) | 10000 + example | SELECT abalance FROM pgbench_accounts WHERE aid = $1 | 10000 + example | UPDATE pgbench_accounts SET abalance = abalance + $1 WHERE aid = $2 | 10000 + example | UPDATE pgbench_branches SET bbalance = bbalance + $1 WHERE bid = $2 | 10000 + example | UPDATE pgbench_tellers SET tbalance = tbalance + $1 WHERE tid = $2 | 10000 + example | alter table pgbench_accounts add primary key (aid) | 1 + example | alter table pgbench_branches add primary key (bid) | 1 + example | alter table pgbench_tellers add primary key (tid) | 1 + example | begin | 10001 + example | commit | 10001 + example | copy pgbench_accounts from stdin with (freeze on) | 1 + example | copy pgbench_branches from stdin with (freeze on) | 1 + example | copy pgbench_tellers from stdin with (freeze on) | 1 + example | create table pgbench_accounts(aid int not null,bid int,abalance int,filler char(84)) with (fillfactor=100) | 1 + example | create table pgbench_branches(bid int not null,bbalance int,filler char(88)) with (fillfactor=100) | 1 + example | create table pgbench_history(tid int,bid int,aid int,delta int,mtime timestamp,filler char(22)) | 1 + example | create table pgbench_tellers(tid int not null,bid int,tbalance int,filler char(84)) with (fillfactor=100) | 1 + example | drop table if exists pgbench_accounts, pgbench_branches, pgbench_history, pgbench_tellers | 1 + example | select count(*) from pgbench_branches | 1 + example | select o.n, p.partstrat, pg_catalog.count(i.inhparent) from pg_catalog.pg_class as c join pg_catalog.pg_namespace as n on (n.oid = c.relnamespace) cr | 1 +(20 rows) + +SELECT pg_stat_monitor_reset(); + pg_stat_monitor_reset +----------------------- + +(1 row) + +SELECT name, setting, unit, context, vartype, source, min_val, max_val, enumvals, boot_val, reset_val, pending_restart FROM pg_settings WHERE name='pg_stat_monitor.pgsm_query_shared_buffer'; + name | setting | unit | context | vartype | source | min_val | max_val | enumvals | boot_val | reset_val | pending_restart +------------------------------------------+---------+------+------------+---------+--------------------+---------+---------+----------+----------+-----------+----------------- + pg_stat_monitor.pgsm_query_shared_buffer | 20 | MB | postmaster | integer | configuration file | 1 | 10000 | | 20 | 20 | f +(1 row) + +SELECT datname, substr(query,0,150) AS query, SUM(calls) AS calls FROM pg_stat_monitor GROUP BY datname, query ORDER BY datname, query, calls DESC Limit 20; + datname | query | calls +---------+-------------------------------------------------------------------------------------------------------------------------------------------------------+------- + example | INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES ($1, $2, $3, $4, CURRENT_TIMESTAMP) | 10000 + example | SELECT abalance FROM pgbench_accounts WHERE aid = $1 | 10000 + example | UPDATE pgbench_accounts SET abalance = abalance + $1 WHERE aid = $2 | 10000 + example | UPDATE pgbench_branches SET bbalance = bbalance + $1 WHERE bid = $2 | 10000 + example | UPDATE pgbench_tellers SET tbalance = tbalance + $1 WHERE tid = $2 | 10000 + example | alter table pgbench_accounts add primary key (aid) | 1 + example | alter table pgbench_branches add primary key (bid) | 1 + example | alter table pgbench_tellers add primary key (tid) | 1 + example | begin | 10001 + example | commit | 10001 + example | copy pgbench_accounts from stdin with (freeze on) | 1 + example | copy pgbench_branches from stdin with (freeze on) | 1 + example | copy pgbench_tellers from stdin with (freeze on) | 1 + example | create table pgbench_accounts(aid int not null,bid int,abalance int,filler char(84)) with (fillfactor=100) | 1 + example | create table pgbench_branches(bid int not null,bbalance int,filler char(88)) with (fillfactor=100) | 1 + example | create table pgbench_history(tid int,bid int,aid int,delta int,mtime timestamp,filler char(22)) | 1 + example | create table pgbench_tellers(tid int not null,bid int,tbalance int,filler char(84)) with (fillfactor=100) | 1 + example | drop table if exists pgbench_accounts, pgbench_branches, pgbench_history, pgbench_tellers | 1 + example | select count(*) from pgbench_branches | 1 + example | select o.n, p.partstrat, pg_catalog.count(i.inhparent) from pg_catalog.pg_class as c join pg_catalog.pg_namespace as n on (n.oid = c.relnamespace) cr | 1 +(20 rows) + +SELECT pg_stat_monitor_reset(); + pg_stat_monitor_reset +----------------------- + +(1 row) + +SELECT name, setting, unit, context, vartype, source, min_val, max_val, enumvals, boot_val, reset_val, pending_restart FROM pg_settings WHERE name='pg_stat_monitor.pgsm_query_shared_buffer'; + name | setting | unit | context | vartype | source | min_val | max_val | enumvals | boot_val | reset_val | pending_restart +------------------------------------------+---------+------+------------+---------+--------------------+---------+---------+----------+----------+-----------+----------------- + pg_stat_monitor.pgsm_query_shared_buffer | 2048 | MB | postmaster | integer | configuration file | 1 | 10000 | | 20 | 2048 | f +(1 row) + +SELECT datname, substr(query,0,150) AS query, SUM(calls) AS calls FROM pg_stat_monitor GROUP BY datname, query ORDER BY datname, query, calls DESC Limit 20; + datname | query | calls +---------+-------------------------------------------------------------------------------------------------------------------------------------------------------+------- + example | INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES ($1, $2, $3, $4, CURRENT_TIMESTAMP) | 10000 + example | SELECT abalance FROM pgbench_accounts WHERE aid = $1 | 10000 + example | UPDATE pgbench_accounts SET abalance = abalance + $1 WHERE aid = $2 | 10000 + example | UPDATE pgbench_branches SET bbalance = bbalance + $1 WHERE bid = $2 | 10000 + example | UPDATE pgbench_tellers SET tbalance = tbalance + $1 WHERE tid = $2 | 10000 + example | alter table pgbench_accounts add primary key (aid) | 1 + example | alter table pgbench_branches add primary key (bid) | 1 + example | alter table pgbench_tellers add primary key (tid) | 1 + example | begin | 10001 + example | commit | 10001 + example | copy pgbench_accounts from stdin with (freeze on) | 1 + example | copy pgbench_branches from stdin with (freeze on) | 1 + example | copy pgbench_tellers from stdin with (freeze on) | 1 + example | create table pgbench_accounts(aid int not null,bid int,abalance int,filler char(84)) with (fillfactor=100) | 1 + example | create table pgbench_branches(bid int not null,bbalance int,filler char(88)) with (fillfactor=100) | 1 + example | create table pgbench_history(tid int,bid int,aid int,delta int,mtime timestamp,filler char(22)) | 1 + example | create table pgbench_tellers(tid int not null,bid int,tbalance int,filler char(84)) with (fillfactor=100) | 1 + example | drop table if exists pgbench_accounts, pgbench_branches, pgbench_history, pgbench_tellers | 1 + example | select count(*) from pgbench_branches | 1 + example | select o.n, p.partstrat, pg_catalog.count(i.inhparent) from pg_catalog.pg_class as c join pg_catalog.pg_namespace as n on (n.oid = c.relnamespace) cr | 1 +(20 rows) + +DROP EXTENSION pg_stat_monitor;