git push origin develMerge branch 'darkfronza-devel' into devel

pull/159/head
Ibrar Ahmed 2021-12-30 19:33:39 +00:00
commit d3fe5edc80
50 changed files with 2960 additions and 1259 deletions

View File

@ -1,63 +0,0 @@
name: Test-with-pg14-pgdg-packages
on: [push]
jobs:
build:
name: pg14-test-with-pgdg-packages
runs-on: ubuntu-latest
steps:
- name: Clone pg_stat_monitor repository
uses: actions/checkout@v2
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 rm -rf /var/lib/postgresql/
sudo rm -rf /var/log/postgresql/
sudo rm -rf /etc/postgresql/
sudo rm -rf /usr/lib/postgresql
sudo rm -rf /usr/include/postgresql
sudo rm -rf /usr/share/postgresql
sudo rm -rf /etc/postgresql
sudo rm -f /usr/bin/pg_config
- name: Install PG Distribution Postgresql 14
run: |
sudo sh -c 'echo "deb http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main 14" > /etc/apt/sources.list.d/pgdg.list'
wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -
sudo apt-get update
sudo apt-get -y install postgresql-14
sudo apt-get update
sudo apt-get -y install postgresql-client-14
sudo apt-get update
sudo apt-get -y install postgresql-server-dev-14
sudo chown -R postgres:postgres src/
- name: Build pg_stat_monitor
run: |
export PATH="/usr/lib/postgresql/14/bin:$PATH"
sudo cp /usr/lib/postgresql/14/bin/pg_config /usr/bin
sudo 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/14/main/postgresql.conf
sudo service postgresql start
sudo -u postgres bash -c 'make installcheck USE_PGXS=1'
working-directory: src/pg_stat_monitor/
- name: Report on test fail
uses: actions/upload-artifact@v2
if: ${{ failure() }}
with:
name: Regressions diff and postgresql log
path: |
src/pg_stat_monitor/regression.diffs
src/pg_stat_monitor/logfile
retention-days: 1

View File

@ -1,4 +1,4 @@
name: Test-with-pg11-build
name: postgresql-11-build
on: [push]
jobs:

View File

@ -1,9 +1,9 @@
name: pg11package-test
name: postgresql-11-package
on: [push]
jobs:
build:
name: pg11package-test
name: postgresql-11-package
runs-on: ubuntu-20.04
steps:
- name: Clone pg_stat_monitor repository

View File

@ -1,9 +1,9 @@
name: Test-with-pg11-pgdg-packages
name: postgresql-11-pgdg-package
on: [push]
jobs:
build:
name: pg11-test-with-pgdg-packages
name: postgresql-11-pgdg-package
runs-on: ubuntu-18.04
steps:
- name: Clone pg_stat_monitor repository

View File

@ -1,9 +1,9 @@
name: Test-with-pg12-build
name: postgresql-12-build
on: [push]
jobs:
build:
name: pg12-test
name: postgresql-12-build
runs-on: ubuntu-latest
steps:
- name: Clone postgres repository

View File

@ -1,9 +1,9 @@
name: pg12package-test
name: postgresql-12-package
on: [push]
jobs:
build:
name: pg12package-test
name: postgresql-12-package
runs-on: ubuntu-20.04
steps:
- name: Clone pg_stat_monitor repository

View File

@ -1,9 +1,9 @@
name: Test-with-pg12-pgdg-packages
name: postgresql-12-pgdg-package
on: [push]
jobs:
build:
name: pg12-test-with-pgdg-packages
name: postgresql-12-pgdg-package
runs-on: ubuntu-latest
steps:
- name: Clone pg_stat_monitor repository

View File

@ -1,9 +1,9 @@
name: Test-with-pg13-build
name: postgresql-13-build
on: [push]
jobs:
build:
name: pg13-test
name: postgresql-13-build
runs-on: ubuntu-latest
steps:
- name: Clone postgres repository

View File

@ -1,9 +1,9 @@
name: pg13package-test
name: postgresql-13-package
on: [push]
jobs:
build:
name: pg13package-test
name: postgresql-13-package
runs-on: ubuntu-20.04
steps:
- name: Clone pg_stat_monitor repository

View File

@ -1,9 +1,9 @@
name: Test-with-pg13-pgdg-packages
name: postgresql-13-pgdg-package
on: [push]
jobs:
build:
name: pg13-test-with-pgdg-packages
name: postgresql-13-pgdg-package
runs-on: ubuntu-latest
steps:
- name: Clone pg_stat_monitor repository

View File

@ -1,9 +1,9 @@
name: Test-with-pg14-build
name: postgresql-14-build
on: [push]
jobs:
build:
name: pg14-test
name: postgresql-14-build
runs-on: ubuntu-latest
steps:
- name: Clone postgres repository

View File

@ -64,6 +64,8 @@ To build `pg_stat_monitor` from source code, you require the following:
* git
* make
* gcc
* pg_config
Refer to the [Building from source code](https://github.com/percona/pg_stat_monitor#installing-from-source-code) section for guidelines.

View File

@ -1,7 +1,7 @@
# contrib/pg_stat_monitor/Makefile
MODULE_big = pg_stat_monitor
OBJS = hash_query.o guc.o pg_stat_monitor.o $(WIN32RES)
OBJS = hash_query.o guc.o pgsm_errors.o pg_stat_monitor.o $(WIN32RES)
EXTENSION = pg_stat_monitor
DATA = pg_stat_monitor--1.0.sql
@ -17,8 +17,28 @@ REGRESS = basic version guc counters relations database top_query application_na
# which typical installcheck users do not have (e.g. buildfarm clients).
# NO_INSTALLCHECK = 1
ifdef USE_PGXS
PG_CONFIG = pg_config
PG_VERSION := $(shell pg_config --version | awk {'print $$1 $$2'})
MAJOR := $(shell echo $(PG_VERSION) | sed -e 's/\.[^./]*$$//')
ifneq (,$(findstring PostgreSQL14,$(MAJOR)))
CP := $(shell cp pg_stat_monitor--1.0.14.sql.in pg_stat_monitor--1.0.sql)
endif
ifneq (,$(findstring PostgreSQL13,$(MAJOR)))
CP := $(shell cp pg_stat_monitor--1.0.13.sql.in pg_stat_monitor--1.0.sql)
endif
ifneq (,$(findstring PostgreSQL12,$(MAJOR)))
CP := $(shell cp pg_stat_monitor--1.0.sql.in pg_stat_monitor--1.0.sql)
endif
ifneq (,$(findstring PostgreSQL11,$(MAJOR)))
CP := $(shell cp pg_stat_monitor--1.0.sql.in pg_stat_monitor--1.0.sql)
endif
ifdef USE_PGXS
PGXS := $(shell $(PG_CONFIG) --pgxs)
include $(PGXS)
else

View File

@ -1,10 +1,8 @@
![PostgreSQL-11](https://github.com/percona/pg_stat_monitor/workflows/pg11-test/badge.svg)
![PostgreSQL-12](https://github.com/percona/pg_stat_monitor/workflows/pg12-test/badge.svg)
![PostgreSQL-13](https://github.com/percona/pg_stat_monitor/workflows/pg13-test/badge.svg)
![PostgreSQL-14](https://github.com/percona/pg_stat_monitor/workflows/pg14-test/badge.svg)
![PostgreSQL-11-Package](https://github.com/percona/pg_stat_monitor/workflows/pg11package-test/badge.svg)
![PostgreSQL-12-Packages](https://github.com/percona/pg_stat_monitor/workflows/pg12package-test/badge.svg)
![PostgreSQL-13-Packages](https://github.com/percona/pg_stat_monitor/workflows/pg13package-test/badge.svg)
![PostgreSQL-11](https://github.com/percona/pg_stat_monitor/workflows/postgresql-11-build/badge.svg) ![PostgreSQL-11-Package](https://github.com/percona/pg_stat_monitor/workflows/postgresql-11-package/badge.svg) ![PostgreSQL-12](https://github.com/percona/pg_stat_monitor/workflows/postgresql-12-build/badge.svg) ![PostgreSQL-12-Package](https://github.com/percona/pg_stat_monitor/workflows/postgresql-12-package/badge.svg)
![PostgreSQL-13](https://github.com/percona/pg_stat_monitor/workflows/postgresql-13-build/badge.svg) ![PostgreSQL-13-Package](https://github.com/percona/pg_stat_monitor/workflows/postgresql-13-package/badge.svg) ![PostgreSQL-14](https://github.com/percona/pg_stat_monitor/workflows/postgresql-14-build/badge.svg)
[![Coverage Status](https://coveralls.io/repos/github/percona/pg_stat_monitor/badge.svg)](https://coveralls.io/github/percona/pg_stat_monitor)
@ -22,8 +20,10 @@
* [Setup](#setup)
* [Building from source code](#building-from-source)
* [How to contribute](#how-to-contribute)
* [Report a Bug](#report-a-bug)
* [Support, discussions and forums](#support-discussions-and-forums)
* [License](#license)
* [Copyright](#copyright)
* [Copyright notice](#copyright-notice)
## Overview
@ -160,7 +160,11 @@ ALTER SYSTEM SET shared_preload_libraries = 'pg_stat_monitor';
ALTER SYSTEM
```
**NOTE**: If youve added other values to the shared_preload_libraries parameter, list all of them separated by commas for the `ALTER SYSTEM` command. For example, `ALTER SYSTEM SET shared_preload_libraries = 'foo, bar, pg_stat_monitor'`
> **NOTE**: If youve added other modules to the `shared_preload_libraries` parameter (for example, `pg_stat_statements`), list all of them separated by commas for the `ALTER SYSTEM` command.
>
>:warning: For PostgreSQL 13 and earlier versions,`pg_stat_monitor` **must** follow `pg_stat_statements`. For example, `ALTER SYSTEM SET shared_preload_libraries = 'foo, pg_stat_statements, pg_stat_monitor'`.
>
>In PostgreSQL 14, modules can be specified in any order.
Start or restart the `postgresql` instance to apply the changes.
@ -188,6 +192,7 @@ CREATE EXTENSION
This allows you to see the stats collected by `pg_stat_monitor`.
By default, `pg_stat_monitor` is created for the `postgres` database. To access the statistics from other databases, you need to create the extension for every database.
```
-- Select some of the query information, like client_ip, username and application_name etc.
@ -209,6 +214,13 @@ To learn more about `pg_stat_monitor` features and usage, see [User Guide](https
### Building from source
To build `pg_stat_monitor` from source code, you require the following:
* git
* make
* gcc
* pg_config
You can download the source code of the latest release of `pg_stat_monitor` from [the releases page on GitHub](https://github.com/Percona/pg_stat_monitor/releases) or using git:
@ -230,6 +242,10 @@ We welcome and strongly encourage community participation and contributions, and
The [Contributing Guide](https://github.com/percona/pg_stat_monitor/blob/master/CONTRIBUTING.md) contains the guidelines on how you can contribute.
### Report a Bug
Please report all bugs to Percona's Jira: https://jira.percona.com/projects/PG/issues
### Support, discussions and forums

80
docs/COMPARISON.md Normal file
View File

@ -0,0 +1,80 @@
# Comparing pg_stat_monitor and pg_stat_statements
The `pg_stat_monitor` extension is developed on the basis of `pg_stat_statements` as its more advanced replacement.
Thus, `pg_stat_monitor` inherits the columns available in `pg_stat_statements` plus provides additional ones.
To see all available columns, run the following command from the `psql` terminal:
```sql
postgres=# \d pg_stat_monitor;
```
The following table compares the `pg_stat_monitor` view with that of `pg_stat_statements`.
Note that the column names differ depending on the PostgreSQL version you are running.
| Column name for PostgreSQL 13 and above | Column name for PostgreSQL 11 and 12 | pg_stat_monitor | pg_stat_statements
|--------------------|--------------------------|-----------------------------|----------------------
bucket | bucket | :heavy_check_mark: | :x:
bucket_start_time | bucket_start_time | :heavy_check_mark: | :x:
userid | userid | :heavy_check_mark: | :heavy_check_mark:
datname | datname | :heavy_check_mark: | :heavy_check_mark:
toplevel[^1] | | :heavy_check_mark: | :heavy_check_mark:
client_ip | client_ip | :heavy_check_mark:| :x:
queryid | queryid | :heavy_check_mark: | :heavy_check_mark:
planid | planid | :heavy_check_mark:| :x:
query_plan | query_plan | :heavy_check_mark: | :x:
top_query | top_query | :heavy_check_mark: | :x:
top_queryid | top_queryid | :heavy_check_mark: | :x:
query | query | :heavy_check_mark: | :heavy_check_mark:
application_name | application_name | :heavy_check_mark:| :x:
relations | relations | :heavy_check_mark: | :x:
cmd_type | cmd_type | :heavy_check_mark: | :x:
elevel | elevel | :heavy_check_mark: | :x:
sqlcode | sqlcode | :heavy_check_mark: | :x:
message | message | :heavy_check_mark: | :x:
plans_calls | plans_calls | :heavy_check_mark: | :heavy_check_mark:
total_plan_time | | :heavy_check_mark: | :heavy_check_mark:
min_plan_time | | :heavy_check_mark: | :heavy_check_mark:
max_plan_time | | :heavy_check_mark: | :heavy_check_mark:
mean_plan_time | | :heavy_check_mark: | :heavy_check_mark:
stddev_plan_time | | :heavy_check_mark: | :heavy_check_mark:
calls | calls | :heavy_check_mark: | :heavy_check_mark:
total_exec_time | total_time | :heavy_check_mark: | :heavy_check_mark:
min_exec_time | min_time | :heavy_check_mark: | :heavy_check_mark:
max_exec_time | max_time | :heavy_check_mark: | :heavy_check_mark:
mean_exec_time | mean_time | :heavy_check_mark: | :heavy_check_mark:
stddev_exec_time | stddev_time | :heavy_check_mark: | :heavy_check_mark:
rows_retrieved | rows_retrieved | :heavy_check_mark: | :heavy_check_mark:
shared_blks_hit | shared_blks_hit | :heavy_check_mark: | :heavy_check_mark:
shared_blks_read | shared_blks_read | :heavy_check_mark: | :heavy_check_mark:
shared_blks_dirtied | shared_blks_dirtied | :heavy_check_mark: | :heavy_check_mark:
shared_blks_written | shared_blks_written | :heavy_check_mark: | :heavy_check_mark:
local_blks_hit | local_blks_hit | :heavy_check_mark: | :heavy_check_mark:
local_blks_read | local_blks_read | :heavy_check_mark: | :heavy_check_mark:
local_blks_dirtied | local_blks_dirtied | :heavy_check_mark: | :heavy_check_mark:
local_blks_written | local_blks_written | :heavy_check_mark: | :heavy_check_mark:
temp_blks_read | temp_blks_read | :heavy_check_mark: | :heavy_check_mark:
temp_blks_written | temp_blks_written | :heavy_check_mark:  | :heavy_check_mark:
blk_read_time | blk_read_time | :heavy_check_mark: | :heavy_check_mark:
blk_write_time | blk_write_time | :heavy_check_mark: | :heavy_check_mark:
resp_calls | resp_calls | :heavy_check_mark: | :x:
cpu_user_time | cpu_user_time | :heavy_check_mark: | :x:
cpu_sys_time | cpu_sys_time | :heavy_check_mark: | :x:
wal_records | wal_records | :heavy_check_mark:  | :heavy_check_mark:
wal_fpi | wal_fpi | :heavy_check_mark:  | :heavy_check_mark:
wal_bytes | wal_bytes | :heavy_check_mark:  | :heavy_check_mark:
state_code | state_code | :heavy_check_mark: | :x:
state | state | :heavy_check_mark: | :x:
To learn more about the features in `pg_stat_monitor`, please see the [User guide](https://github.com/percona/pg_stat_monitor/blob/master/docs/USER_GUIDE.md).
Additional reading: [pg_stat_statements](https://www.postgresql.org/docs/current/pgstatstatements.html)
[^1]: Available starting from PostgreSQL 14 and above

66
docs/REFERENCE.md Normal file
View File

@ -0,0 +1,66 @@
# `pg_stat_monitor` view reference
`pg_stat_monitor` provides a view where the statistics data is displayed. To see all available columns, run the following command from `psql`:
```sql
postgres=# \d pg_stat_monitor
```
Depending on the PostgreSQL version, some column names may differ. The following table describes the `pg_stat_monitor` view for PostgreSQL 14 and higher versions.
| Column | Type | Description
|--------------------|--------------------------|------------------
bucket | integer | Data collection unit. The number shows what bucket in a chain a record belongs to
bucket_start_time | timestamp with time zone | The start time of the bucket|
userid | regrole | An ID of the user who run a query |
datname | name | The name of a database where the query was executed
toplevel | bool | True means that a query was executed as a top-level statement
client_ip | inet | The IP address of a client that run the query
queryid | text | The internal hash code serving to identify every query in a statement
planid | text | An internally generated ID of a query plan
query_plan | text | The sequence of steps used to execute a query. This parameter is available only when the `pgsm_enable_query_plan` is enabled.
top_query | text | Shows the top query used in a statement |
query | text | The actual text of the query |
application_name | text | Shows the name of the application connected to the database
relations | text[] | The list of tables involved in the query
cmd_type | integer | Type of the query executed
cmd_type_text | text[] | The description of the query executed
elevel | integer | Shows the error level of a query (WARNING, ERROR, LOG)
sqlcode | integer | SQL error code
message | text | The error message
plans_calls | bigint | The number of times the statement was planned
total_plan_time | double precision | The total time (in ms) spent on planning the statement
min_plan_time | double precision | Minimum time (in ms) spent on planning the statement
max_plan_time | double precision | Maximum time (in ms) spent on planning the statement
mean_plan_time | double precision | The mean (average) time (in ms) spent on planning the statement
stddev_plan_time | double precision | The standard deviation of time (in ms) spent on planning the statement
calls | bigint | The number of times a particular query was executed
total_exec_time | double precision | The total time (in ms) spent on executing a query
min_exec_time | double precision | The minimum time (in ms) it took to execute a query
max_exec_time | double precision | The maximum time (in ms) it took to execute a query
mean_time | double precision | The mean (average) time (in ms) it took to execute a query
stddev_exec_time | double precision | The standard deviation of time (in ms) spent on executing a query
rows_retrieved | bigint | The number of rows retrieved when executing a query
shared_blks_hit | bigint | Shows the total number of shared memory blocks returned from the cache
shared_blks_read | bigint | Shows the total number of shared blocks returned not from the cache
shared_blks_dirtied | bigint | Shows the number of shared memory blocks "dirtied" by the query execution (i.e. a query modified at least one tuple in a block and this block must be written to a drive)
shared_blks_written | bigint | Shows the number of shared memory blocks written simultaneously to a drive during the query execution
local_blks_hit | bigint | The number of blocks which are considered as local by the backend and thus are used for temporary tables
local_blks_read | bigint | Total number of local blocks read during the query execution
local_blks_dirtied | bigint | Total number of local blocks "dirtied" during the query execution (i.e. a query modified at least one tuple in a block and this block must be written to a drive)
local_blks_written | bigint | Total number of local blocks written simultaneously to a drive during the query execution
temp_blks_read | bigint | Total number of blocks of temporary files read from a drive. Temporary files are used when there's not enough memory to execute a query
temp_blks_written | bigint | Total number of blocks of temporary files written to a drive
blk_read_time | double precision | Total waiting time (in ms) for reading blocks
blk_write_time | double precision | Total waiting time (in ms) for writing blocks to a drive
resp_calls | text[] | Call histogram
cpu_user_time | double precision | The time (in ms) the CPU spent on running the query
cpu_sys_time | double precision | The time (in ms) the CPU spent on executing the kernel code
wal_records | bigint | The total number of WAL (Write Ahead Logs) generated by the query
wal_fpi | bigint | The total number of WAL FPI (Full Page Images) generated by the query
wal_bytes | numeric | Total number of bytes used for the WAL generated by the query
state_code | bigint | Shows the state code of a query
state | text | The state message

View File

@ -1,170 +1,334 @@
# User Guide
This document describes the configuration, key features and usage of ``pg_stat_monitor`` extension and compares it with ``pg_stat_statements``.
* [Introduction](#introduction)
* [Features](#features)
* [Views](#views)
* [Functions](#functions)
* [Configuration](#configuration)
* [Usage examples](#usage-examples)
For how to install and set up ``pg_stat_monitor``, see [README](https://github.com/percona/pg_stat_monitor/blob/master/README.md).
## Introduction
After you've installed and enabled ``pg_stat_monitor``, create the ``pg_stat_monitor`` extension using the ``CREATE EXTENSION`` command.
This document describes the features, functions and configuration of the ``pg_stat_monitor`` extension and gives some usage examples. For how to install and set up ``pg_stat_monitor``, see [Installation in README](https://github.com/percona/pg_stat_monitor/blob/master/README.md#installation).
```sql
CREATE EXTENSION pg_stat_monitor;
CREATE EXTENSION
## Features
The following are the key features of pg_stat_monitor:
* [Time buckets](#time-buckets),
* [Table and index access statistics per statement](#table-and-index-access-statistics-per-statement),
* Query statistics:
* [Query and client information](#query-and-client-information),
* [Query timing information](#query-timing-information),
* [Query execution plan information](#query-execution-plan-information),
* [Use of actual data or parameters placeholders in queries](#use-of-actual-data-or-parameters-placeholders-in-queries),
* [Query type filtering](#query-type-filtering),
* [Query metadata supporting Googles Sqlcommentor](#query-metadata),
* [Top query tracking](#top-query-tracking),
* [Relations](#relations) - showing tables involved in a query,
* [Monitoring of queries terminated with ERROR, WARNING and LOG error levels](#monitoring-of-queries-terminated-with-error-warning-and-log-error-levels),
* [Integration with Percona Monitoring and Management (PMM) tool](#integration-with-pmm),
* [Histograms](#histogram) - visual representation of query performance.
### Time buckets
Instead of supplying one set of ever-increasing counts, `pg_stat_monitor` computes stats for a configured number of time intervals; time buckets. This allows for much better data accuracy, especially in the case of high-resolution or unreliable networks.
### Table and index access statistics per statement
`pg_stat_monitor` collects the information about what tables were accessed by a statement. This allows you to identify all queries which access a given table easily.
### Query and client information
`pg_stat_monitor` provides additional metrics for detailed analysis of query performance from various perspectives, including client connection details like user name, application name, IP address to name a few relevant columns.
With this information, `pg_stat_monitor` enables users to track a query to the originating application. More details about the application or query may be incorporated in the SQL query in a [Googles Sqlcommenter](https://google.github.io/sqlcommenter/) format.
To see how it works, refer to the [usage example](#query-information)
### Query timing information
Understanding query execution time stats helps you identify what affects query performance and take measures to optimize it. `pg_stat_monitor` collects the total, min, max and average (mean) time it took to execute a particular query and provides this data in separate columns. See the [Query timing information](#query-timing-information-1) example for the sample output.
### Query execution plan information
Every query has a plan that was constructed for its executing. Collecting the query plan information as well as monitoring query plan timing helps you understand how you can modify the query to optimize its execution. It also helps make communication about the query clearer when discussing query performance with other DBAs and application developers.
See the [Query execution plan](##query-execution-plan) example for the sample output.
### Use of actual data or parameters placeholders in queries
You can select whether to see queries with parameters placeholders or actual query data. The benefit of having the full query example is in being able to run the [EXPLAIN](https://www.postgresql.org/docs/current/sql-explain.html) command on it to see how its execution was planned. As a result, you can modify the query to make it run better.
### Query type filtering
`pg_stat_monitor` monitors queries per type (``SELECT``, `INSERT`, `UPDATE` or `DELETE`) and classifies them accordingly in the `cmd_type` column. This way you can separate the queries you are interested in and focus on identifying the issues and optimizing query performance.
See the [Query type filtering example](#query-type-filtering-1) for the sample output.
### Query metadata
Googles Sqlcommenter is a useful tool that in a way bridges that gap between ORM libraries and understanding database performance. And ``pg_stat_monitor`` supports it. So, you can now put any key-value data (like what client executed a query or if it is testing vs production query) in the comments in `/* … */` syntax in your SQL statements, and the information will be parsed by `pg_stat_monitor` and made available in the comments column in the `pg_stat_monitor` view. For details on the comments syntax, see [Sqlcommenter documentation](https://google.github.io/sqlcommenter/).
To see how it works, see the [Query metadata](#query-metadata-1) example.
### Top query tracking
Using functions is common. While running, functions can execute queries internally. `pg_stat_monitor` not only keeps track of all executed queries within a function, but also marks that function as top query.
Top query indicates the main query. To illustrate, for the SELECT query that is invoked within a function, the top query is calling this function.
This enables you to backtrack to the originating function and thus simplifies the tracking and analysis.
Find more details in the [usage example](#top-query-tracking-1).
### Relations
`pg_stat_monitor` provides the list of tables involved in the query in the relations column. This reduces time on identifying the tables and simplifies the analysis. To learn more, see the [usage examples](#relations-1)
### Monitoring queries terminated with ERROR, WARNING and LOG error levels
Monitoring queries that terminate with ERROR, WARNING, LOG states can give useful information to debug an issue. Such messages have the error level (`elevel`), error code (`sqlcode`), and error message (`message`). `pg_stat_monitor` collects all this information and aggregates it so that you can measure performance for successful and failed queries separately, as well as understand why a particular query failed to execute successfully.
Find details in the [usage example](#queries-terminated-with-errors)
### Integration with PMM
To timely identify and react on issues, performance should be automated and alerts should be sent when an issue occurs. There are many monitoring tools available for PostgreSQL, some of them (like Nagios) supporting custom metrics provided via extensions. Though you can integrate `pg_stat_monitor` with these tools, it natively supports integration with Percona Management and Monitoring (PMM). This integration allows you to enjoy all the features provided by both solutions: advanced statistics data provided by `pg_stat_monitor` and automated monitoring with data visualization on dashboards, security threat checks and alerting, available in PMM out of the box.
To learn how to integrate pg_stat_monitor with PMM, see [Configure pg_stat_monitor in PMM](https://www.percona.com/doc/percona-monitoring-and-management/2.x/setting-up/client/postgresql.html#pg_stat_monitor)
### Histogram
Histogram (the `resp_calls` parameter) provides a visual representation of query performance. With the help of the histogram function, you can view a timing/calling data histogram in response to an SQL query.
Learn more about using histograms from the [usage example](#histogram-1).
## Views
`pg_stat_monitor` provides the following views:
* `pg_stat_monitor` is the view where statistics data is presented.
* `pg_stat_monitor_settings` view shows available configuration options which you can change.
### `pg_stat_monitor` view
The statistics gathered by the module are made available via the view named `pg_stat_monitor`. This view contains one row for each distinct combination of metrics and whether it is a top-level statement or not (up to the maximum number of distinct statements that the module can track). For details about available counters, refer to the [`pg_stat_monitor` view reference](https://github.com/percona/pg_stat_monitor/blob/master/docs/REFERENCE.md).
The following are the primary keys for pg_stat_monitor:
* `bucket`,
* `userid`,
* `dbid`,
* `client_ip`,
* `application_name`.
A new row is created for each key in the `pg_stat_monitor` view.
`pg_stat_monitor` inherits the metrics available in `pg_stat_statements`, plus provides additional ones. See the [`pg_stat_monitor` vs `pg_stat_statements` comparison](https://github.com/percona/pg_stat_monitor/blob/master/docs/REFERENCE.md) for details.
For security reasons, only superusers and members of the `pg_read_all_stats` role are allowed to see the SQL text and `queryid` of queries executed by other users. Other users can see the statistics, however, if the view has been installed in their database.
### pg_stat_monitor_settings view
The `pg_stat_monitor_settings` view shows one row per `pg_stat_monitor` configuration parameter. It displays configuration parameter name, value, default value, description, minimum and maximum values, and whether a restart is required for a change in value to be effective.
## Functions
### pg_stat_monitor_reset()
This function resets all the statistics and clears the view. Eventually, the function will delete all the previous data.
### pg_stat_monitor_version()
This function provides the build version of `pg_stat_monitor` version.
```
postgres=# select pg_stat_monitor_version();
pg_stat_monitor_version
-------------------------
devel
(1 row)
```
### Configuration
Here is the complete list of configuration parameters.
### histogram(bucket id, query id)
It is used to generate the histogram, you can refer to histogram sections.
## Configuration
Use the following command to view available configuration parameters in the `pg_stat_monitor_settings` view:
```sql
SELECT * FROM pg_stat_monitor_settings;
name | value | default_value | description | minimum | maximum | restart
------------------------------------------+--------+---------------+----------------------------------------------------------------------------------------------------------+---------+------------+---------
pg_stat_monitor.pgsm_max | 100 | 100 | Sets the maximum size of shared memory in (MB) used for statement's metadata tracked by pg_stat_monitor. | 1 | 1000 | 1
pg_stat_monitor.pgsm_query_max_len | 1024 | 1024 | Sets the maximum length of query. | 1024 | 2147483647 | 1
pg_stat_monitor.pgsm_enable | 1 | 1 | Enable/Disable statistics collector. | 0 | 0 | 0
pg_stat_monitor.pgsm_track_utility | 1 | 1 | Selects whether utility commands are tracked. | 0 | 0 | 0
pg_stat_monitor.pgsm_normalized_query | 1 | 1 | Selects whether save query in normalized format. | 0 | 0 | 0
pg_stat_monitor.pgsm_max_buckets | 10 | 10 | Sets the maximum number of buckets. | 1 | 10 | 1
pg_stat_monitor.pgsm_bucket_time | 60 | 60 | Sets the time in seconds per bucket. | 1 | 2147483647 | 1
pg_stat_monitor.pgsm_histogram_min | 0 | 0 | Sets the time in millisecond. | 0 | 2147483647 | 1
pg_stat_monitor.pgsm_histogram_max | 100000 | 100000 | Sets the time in millisecond. | 10 | 2147483647 | 1
pg_stat_monitor.pgsm_histogram_buckets | 10 | 10 | Sets the maximum number of histogram buckets | 2 | 2147483647 | 1
pg_stat_monitor.pgsm_query_shared_buffer | 20 | 20 | Sets the maximum size of shared memory in (MB) used for query tracked by pg_stat_monitor. | 1 | 10000 | 1
pg_stat_monitor.pgsm_overflow_target | 1 | 1 | Sets the overflow target for pg_stat_monitor | 0 | 1 | 1
pg_stat_monitor.pgsm_track_planning | 0 | 1 | Selects whether planning statistics are tracked. | 0 | 0 | 0
(13 rows)
```
Some configuration parameters require a server restart and should be set before the server startup. These must be set in the ``postgresql.conf`` file. Other parameters do not require a server restart and can be set permanently either in the ``postgresql.conf`` or from the client (``psql``).
The table below shows set up options for each configuration parameter and whether the server restart is required to apply the parameter's value:
To amend the `pg_stat_monitor` configuration, use the General Configuration Unit (GCU) system. Some configuration parameters require the server restart and should be set before the server startup. These must be set in the `postgresql.conf` file. Other parameters do not require server restart and can be set permanently either in the `postgresql.conf` or from the client (`psql`) using the SET or ALTER SYSTEM SET commands.
The following table shows setup options for each configuration parameter and whether the server restart is required to apply the parameter's value:
| Parameter Name                                |  postgresql.conf   | SET | ALTER SYSTEM SET  |  server restart   | configuration reload
| Parameter Name | postgresql.conf | SET | ALTER SYSTEM SET | server restart | configuration reload
| ----------------------------------------------|--------------------|-----|-------------------|-------------------|---------------------
| pg_stat_monitor.pgsm_max                      | :heavy_check_mark: | :x:                |:x:                |:heavy_check_mark: | :x:
| pg_stat_monitor.pgsm_query_max_len            | :heavy_check_mark: | :x:                |:x:                |:heavy_check_mark: | :x:
| pg_stat_monitor.pgsm_enable                   | :heavy_check_mark: | :x:                |:heavy_check_mark: |:x: | :x:
| pg_stat_monitor.pgsm_track_utility            | :heavy_check_mark: | :heavy_check_mark: |:heavy_check_mark: |:x: | :heavy_check_mark:
| pg_stat_monitor.pgsm_normalized_query         | :heavy_check_mark: | :heavy_check_mark: |:heavy_check_mark: |:x: | :heavy_check_mark:
| pg_stat_monitor.pgsm_max_buckets              | :heavy_check_mark: | :x:                |:x:                |:heavy_check_mark: | :heavy_check_mark:
| pg_stat_monitor.pgsm_bucket_time              | :heavy_check_mark: | :x:                |:x:                |:heavy_check_mark: | :x:
| pg_stat_monitor.pgsm_object_cache             | :heavy_check_mark: | :x:                |:x:                |:heavy_check_mark: | :x:
| pg_stat_monitor.pgsm_respose_time_lower_bound | :heavy_check_mark: | :x:                |:x:                |:heavy_check_mark: | :x:
| pg_stat_monitor.pgsm_respose_time_step        | :heavy_check_mark: | :x:                |:x:                |:heavy_check_mark: | :x:
| pg_stat_monitor.pgsm_query_shared_buffer      | :heavy_check_mark: | :x:                |:x:                |:heavy_check_mark: | :x:
 
| [pg_stat_monitor.pgsm_max](#pg_stat_monitorpgsm_max) | :heavy_check_mark: | :x: |:x: |:heavy_check_mark: | :x:
| [pg_stat_monitor.pgsm_query_max_len](#pg_stat_monitorpgsm_query_max_len) | :heavy_check_mark: | :x: |:x: |:heavy_check_mark: | :x:
| [pg_stat_monitor.pgsm_enable](#pg_stat_monitorpgsm_enable) | :heavy_check_mark: | :x: |:heavy_check_mark: |:x: | :x:
| [pg_stat_monitor.pgsm_track_utility](#pg_stat_monitorpgsm_track_utility) | :heavy_check_mark: | :heavy_check_mark: |:heavy_check_mark: |:x: | :heavy_check_mark:
| [pg_stat_monitor.pgsm_normalized_query](#pg_stat_monitorpgsm_normalized_query) | :heavy_check_mark: | :heavy_check_mark: |:heavy_check_mark: |:x: | :heavy_check_mark:
| [pg_stat_monitor.pgsm_max_buckets](#pg_stat_monitorpgsm_max_buckets) | :heavy_check_mark: | :x: |:x: |:heavy_check_mark: | :heavy_check_mark:
| [pg_stat_monitor.pgsm_bucket_time](#pg_stat_monitorpgsm_bucket_time) | :heavy_check_mark: | :x: |:x: |:heavy_check_mark: | :x:
| [pg_stat_monitor.pgsm_object_cache](#pg-stat-monitorpgsm-object-cache) | :heavy_check_mark: | :x: |:x: |:heavy_check_mark: | :x:
| [pg_stat_monitor.pgsm_histogram_min](#pg_stat_monitorpgsm_histogram_min) | :heavy_check_mark: | :x: |:x: |:heavy_check_mark: | :x:
| [pg_stat_monitor.pgsm_histogram_max](#pg_stat_monitorpgsm_histogram_max) | :heavy_check_mark: | :x: |:x: |:heavy_check_mark: | :x:
| [pg_stat_monitor.pgsm_histogram_buckets](#pg_stat_monitorpgsm_histogram_buckets) | :heavy_check_mark: | :x: |:x: |:heavy_check_mark: | :x:
| [pg_stat_monitor.pgsm_query_shared_buffer](#pg_stat_monitorpgsm_query_shared_buffer) | :heavy_check_mark: | :x: |:x: |:heavy_check_mark: | :x:
| [pg_stat_monitor.pgsm_overflow_target](#pg_stat_monitorpgsm_overflow_target) | :heavy_check_mark: | :x: | :x: | :heavy_check_mark: | :x: |
| [pg_stat_monitor.pgsm_enable_query_plan](#pg_stat_monitorpgsm_enable_query_plan) | :heavy_check_mark: | :x: | :x: | :heavy_check_mark: | :x: |
| [pg_stat_monitor.pgsm_track_planning](#pg_stat_monitorpgsm_track_planning) | :heavy_check_mark: | :x: | :x: | :heavy_check_mark: | :x: |
#### Parameters description:
- **pg_stat_monitor.pgsm_max**: This parameter defines the limit of shared memory for ``pg_stat_monitor``. This memory is used by buckets in a circular manner. The memory is divided between the buckets equally, at the start of the PostgreSQL.
- **pg_stat_monitor.pgsm_query_max_len**: Sets the maximum size of the query. This parameter can only be set at the start of PostgreSQL. For long queries, the query is truncated to that particular length. This is to avoid unnecessary usage of shared memory.
- **pg_stat_monitor.pgsm_enable**: This parameter enables or disables the monitoring. "Disable" means that ``pg_stat_monitor`` will not collect the statistics for the whole cluster.
- **pg_stat_monitor.pgsm_track_utility**: This parameter controls whether utility commands are tracked by the module. Utility commands are all those other than ``SELECT``, ``INSERT``, ``UPDATE``, and ``DELETE``.
- **pg_stat_monitor.pgsm_normalized_query**: By default, the query shows the actual parameter instead of the placeholder. It is quite useful when users want to use that query and try to run that query to check the abnormalities. But in most cases users like the queries with a placeholder. This parameter is used to toggle between the two said options.
- **pg_stat_monitor.pgsm_max_buckets**: ``pg_stat_monitor`` accumulates the information in the form of buckets. All the aggregated information is bucket based. This parameter is used to set the number of buckets the system can have. For example, if this parameter is set to 2, then the system will create two buckets. First, the system will add all the information into the first bucket. After its lifetime (defined in the  pg_stat_monitor.pgsm_bucket_time parameter) expires, it will switch to the second bucket,  reset all the counters and repeat the process.
- **pg_stat_monitor.pgsm_bucket_time**: This parameter is used to set the lifetime of the bucket. System switches between buckets on the basis of ``pg_stat_monitor.pgsm_bucket_time``.
- **pg_stat_monitor.pgsm_respose_time_lower_bound**: ``pg_stat_monitor`` also stores the execution time histogram. This parameter is used to set the lower bound of the histogram.
- **pg_stat_monitor.pgsm_respose_time_step:** This parameter is used to set the steps for the histogram.
##### pg_stat_monitor.pgsm_max
Values:
- Min: 1
- Max: 1000
- Default: 100
### Usage
This parameter defines the limit of shared memory (in MB) for ``pg_stat_monitor``. This memory is used by buckets in a circular manner. The memory is divided between the buckets equally, at the start of the PostgreSQL. Requires the server restart.
The ``pg_stat_monitor`` extension contains a view called ``pg_stat_monitor``, which containss all the monitoring information. Find the list of columns in ``pg_stat_monitor`` view in the following table. The table also shows whether a particular column is available in ``pg_stat_statements``.
##### pg_stat_monitor.pgsm_query_max_len
Values:
- Min: 1024
- Max: 2147483647
- Default: 1024
Sets the maximum size of the query. This parameter can only be set at the start of PostgreSQL. For long queries, the query is truncated to that particular length. This is to avoid unnecessary usage of shared memory. Requires the server restart.
##### pg_stat_monitor.pgsm_enable
Type: boolean. Default: 1
Enables or disables the monitoring. "Disable" (0) means that ``pg_stat_monitor`` will not collect the statistics for the whole cluster.
##### pg_stat_monitor.pgsm_track_utility
Type: boolean. Default: 1
This parameter controls whether utility commands are tracked by the module. Utility commands are all those other than ``SELECT``, ``INSERT``, ``UPDATE``, and ``DELETE``.
##### pg_stat_monitor.pgsm_normalized_query
Type: boolean. Default: 1
By default, the query shows the actual parameter instead of the placeholder. It is quite useful when users want to use that query and try to run that query to check the abnormalities. But in most cases users like the queries with a placeholder. This parameter is used to toggle between the two said options.
##### pg_stat_monitor.pgsm_max_buckets
Values:
- Min: 1
- Max: 10
- Default: 10
``pg_stat_monitor`` accumulates the information in the form of buckets. All the aggregated information is bucket based. This parameter is used to set the number of buckets the system can have. For example, if this parameter is set to 2, then the system will create two buckets. First, the system will add all the information into the first bucket. After its lifetime (defined in the [pg_stat_monitor.pgsm_bucket_time](#pg-stat-monitorpgsm-bucket-time) parameter) expires, it will switch to the second bucket, reset all the counters and repeat the process.
Requires the server restart.
##### pg_stat_monitor.pgsm_bucket_time
Values:
- Min: 1
- Max: 2147483647
- Default: 60
This parameter is used to set the lifetime of the bucket. System switches between buckets on the basis of [pg_stat_monitor.pgsm_bucket_time](#pg-stat-monitorpgsm-bucket-time).
Requires the server restart.
##### pg_stat_monitor.pgsm_histogram_min
Values:
- Min: 0
- Max: 2147483647
- Default: 0
``pg_stat_monitor`` also stores the execution time histogram. This parameter is used to set the lower bound of the histogram (in ms).
Requires the server restart.
##### pg_stat_monitor.pgsm_histogram_max
Values:
- Min: 10
- Max: 2147483647
- Default: 100000
This parameter sets the upper bound of the execution time histogram (in ms). Requires the server restart.
##### pg_stat_monitor.pgsm_histogram_buckets
Values:
- Min: 2
- Max: 2147483647
- Default: 10
This parameter sets the maximum number of histogram buckets. Requires the server restart.
##### pg_stat_monitor.pgsm_query_shared_buffer
Values:
- Min: 1
- Max: 10000
- Default: 20
This parameter defines the shared memory limit (in MB) allocated for a query tracked by ``pg_stat_monitor``. Requires the server restart.
##### pg_stat_monitor.pgsm_overflow_target
Type: boolean. Default: 1
Sets the overflow target for the `pg_stat_monitor`. Requires the server restart.
##### pg_stat_monitor.pgsm_enable_query_plan
Type: boolean. Default: 1
Enables or disables query plan monitoring. When the `pgsm_enable_query_plan` is disabled (0), the query plan will not be captured by `pg_stat_monitor`. Enabling it may adversely affect the database performance. Requires the server restart.
|      Column        |           Type           | pg_stat_monitor      | pg_stat_statements
|--------------------|--------------------------|----------------------|------------------
 bucket              | integer                  | :heavy_check_mark:  | :x:
 bucket_start_time   | timestamp with time zone | :heavy_check_mark:  | :x:
 userid              | oid                      | :heavy_check_mark:  | :heavy_check_mark:
 dbid                | oid                      | :heavy_check_mark:  | :heavy_check_mark:
 client_ip           | inet                     | :heavy_check_mark:  | :x:
 queryid             | text                     | :heavy_check_mark:  | :heavy_check_mark:
 planid             | text                     | :heavy_check_mark:  | :x:
 query_plan         | text                     | :heavy_check_mark:  | :x:
 top_query           | text                     | :heavy_check_mark:  | :x:
 query               | text                     | :heavy_check_mark:  | :heavy_check_mark:
 application_name    | text                     | :heavy_check_mark:  | :x:
 relations           | text[]                   | :heavy_check_mark:  | :x:
 cmd_type            | text[]                   | :heavy_check_mark:  | :x:
 elevel              | integer                  | :heavy_check_mark:  | :x:
 sqlcode             | integer                  | :heavy_check_mark:  | :x:
 message             | text                     | :heavy_check_mark:  | :x:
 plans               | bigint                   | :heavy_check_mark:  | :heavy_check_mark:
 plan_total_time     | double precision         | :heavy_check_mark:  | :heavy_check_mark:
 plan_min_time      | double precision         | :heavy_check_mark:  | :heavy_check_mark:
 plan_max_time       | double precision         | :heavy_check_mark:  | :heavy_check_mark:
 plan_mean_time      | double precision         | :heavy_check_mark:  | :heavy_check_mark:
 plan_stddev_time    | double precision         | :heavy_check_mark:  | :heavy_check_mark:
 calls               | bigint                   | :heavy_check_mark:  | :heavy_check_mark:
 total_time          | double precision         | :heavy_check_mark:  | :heavy_check_mark:
 min_time            | double precision         | :heavy_check_mark:  | :heavy_check_mark:
 max_time            | double precision         | :heavy_check_mark:  | :heavy_check_mark:
 mean_time           | double precision         | :heavy_check_mark:  | :heavy_check_mark:
 stddev_time         | double precision         | :heavy_check_mark:  | :heavy_check_mark:
 rows_retrieved      | bigint                   | :heavy_check_mark:  | :heavy_check_mark:
 shared_blks_hit     | bigint                   | :heavy_check_mark:  | :heavy_check_mark:
 shared_blks_read    | bigint                   | :heavy_check_mark:  | :heavy_check_mark:
 shared_blks_dirtied | bigint                   | :heavy_check_mark:  | :heavy_check_mark:
 shared_blks_written | bigint                   | :heavy_check_mark:  | :heavy_check_mark:
 local_blks_hit      | bigint                   | :heavy_check_mark:  | :heavy_check_mark:
 local_blks_read     | bigint                   | :heavy_check_mark:  | :heavy_check_mark:
 local_blks_dirtied  | bigint                   | :heavy_check_mark:  | :heavy_check_mark:
 local_blks_written  | bigint                   | :heavy_check_mark:  | :heavy_check_mark:
 temp_blks_read      | bigint                   | :heavy_check_mark:  | :heavy_check_mark:
 temp_blks_written   | bigint                   | :heavy_check_mark:  | :heavy_check_mark:
 blk_read_time       | double precision         | :heavy_check_mark:  | :heavy_check_mark:
 blk_write_time      | double precision         | :heavy_check_mark:  | :heavy_check_mark:
 resp_calls          | text[]                   | :heavy_check_mark:  | :x:
 cpu_user_time       | double precision         | :heavy_check_mark:  | :x:
 cpu_sys_time        | double precision         | :heavy_check_mark:  | :x:
wal_records | bigint | :heavy_check_mark:  | :heavy_check_mark:
wal_fpi | bigint | :heavy_check_mark:  | :heavy_check_mark:
wal_bytes | numeric | :heavy_check_mark:  | :heavy_check_mark:
state_code | bigint | :heavy_check_mark:  | :x:
 state           | text                   | :heavy_check_mark:  | :x:
##### pg_stat_monitor.pgsm_track_planning
Type: boolean. Default: 0
This parameter instructs ``pg_stat_monitor`` to monitor query planning statistics. Requires the server restart.
The following are some key features of pg_stat_monitor and usage examples.
## Usage examples
#### Buckets
Note that the column names differ depending on the PostgreSQL version you are using. The following usage examples are provided for PostgreSQL version 13.
For versions 11 and 12, please consult the [pg_stat_monitor reference](https://github.com/percona/pg_stat_monitor/blob/master/docs/REFERENCE.md).
**`bucket`**: Accumulates the statistics per bucket. All the information and aggregate reset for each bucket. The bucket will be a number showing the number of buckets for which this record belongs.
**`bucket_start_time`**: shows the start time of the bucket.
### Querying buckets
```sql
postgres=# select bucket, bucket_start_time, query,calls from pg_stat_monitor order by bucket;
bucket | bucket_start_time | query | calls
--------+---------------------+---------------------------------------------------------------------------------------------------------------+-------
3 | 11-01-2021 17:30:45 | copy pgbench_accounts from stdin | 1
3 | 11-01-2021 17:30:45 | alter table pgbench_accounts add primary key (aid) | 1
3 | 11-01-2021 17:30:45 | vacuum analyze pgbench_accounts | 1
3 | 11-01-2021 17:30:45 | vacuum analyze pgbench_tellers | 1
3 | 11-01-2021 17:30:45 | insert into pgbench_branches(bid,bbalance) values($1,$2) | 100
5 | 11-01-2021 17:31:15 | vacuum analyze pgbench_branches | 1
5 | 11-01-2021 17:31:15 | copy pgbench_accounts from stdin | 1
5 | 11-01-2021 17:31:15 | vacuum analyze pgbench_tellers | 1
5 | 11-01-2021 17:31:15 | commit | 1
6 | 11-01-2021 17:31:30 | alter table pgbench_branches add primary key (bid) | 1
6 | 11-01-2021 17:31:30 | vacuum analyze pgbench_accounts | 1
-[ RECORD 1 ]-----+------------------------------------------------------------------------------------
bucket | 0
bucket_start_time | 2021-10-22 11:10:00
query | select bucket, bucket_start_time, query,calls from pg_stat_monitor order by bucket;
calls | 1
```
#### Query Information
The `bucket` parameter shows the number of a bucket for which a given record belongs.
The `bucket_start_time` shows the start time of the bucket.
`query` shows the actual query text.
`calls` shows how many times a given query was called.
**`userid`**: An ID of the user to whom that query belongs. ``pg_stat_monitor`` collects queries from all the users and uses the `userid` to segregate the queries based on different users.
### Query information
**`dbid`**: The database ID of the query. ``pg_stat_monitor`` accumulates queries from all the databases; therefore, this column is used to identify the database.
**Example 1: Shows the usename, database name, unique queryid hash, query, and the total number of calls of that query.**
**`queryid`**: ``pg_stat_monitor`` generates a unique ID for each query (queryid).
**`query`**: The query column contains the actual text of the query. This parameter depends on the **`pg_stat_monitor.pgsm_normalized_query`** configuration parameters, in which format to show the query.
**`calls`**: Number of calls of that particular query.
##### Example: Shows the usename, database name, unique queryid hash, query, and the total number of calls of that query.
```sql
postgres=# SELECT userid, datname, queryid, substr(query,0, 50) AS query, calls FROM pg_stat_monitor;
userid | datname | queryid | query | calls
@ -186,219 +350,43 @@ postgres=# SELECT userid, datname, queryid, substr(query,0, 50) AS query, calls
```
##### Example: Shows the connected application_name.
**Example 2: Shows the connected application details.**
```sql
SELECT application_name, query FROM pg_stat_monitor;
 application_name |                                                query                                                
------------------+------------------------------------------------------------------------------------------------------
 pgbench          | UPDATE pgbench_branches SET bbalance = bbalance + $1 WHERE bid = $2
 pgbench          | UPDATE pgbench_accounts SET abalance = abalance + $1 WHERE aid = $2
 pgbench          | vacuum pgbench_tellers
 pgbench          | SELECT abalance FROM pgbench_accounts WHERE aid = $1
 pgbench          | END
 pgbench          | select count(*) from pgbench_branches
 pgbench          | BEGIN
 pgbench          | INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES ($1, $2, $3, $4, CURRENT_TIMESTAMP)
 psql             | select application_name, query from pg_stat_monitor
 pgbench          | vacuum pgbench_branches
 psql             | select application_name query from pg_stat_monitor
 pgbench          | truncate pgbench_history
 pgbench          | UPDATE pgbench_tellers SET tbalance = tbalance + $1 WHERE tid = $2
postgres=# SELECT application_name, client_ip, substr(query,0,100) as query FROM pg_stat_monitor;
application_name | client_ip | query
------------------+-----------+-----------------------------------------------------------------------------------------------------
pgbench | 127.0.0.1 | truncate pgbench_history
pgbench | 127.0.0.1 | SELECT abalance FROM pgbench_accounts WHERE aid = $1
pgbench | 127.0.0.1 | UPDATE pgbench_accounts SET abalance = abalance + $1 WHERE aid = $2
pgbench | 127.0.0.1 | BEGIN;
pgbench | 127.0.0.1 | INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES ($1, $2, $3, $4, CURRENT_TIMESTAMP
pgbench | 127.0.0.1 | END;
pgbench | 127.0.0.1 | vacuum pgbench_branches
pgbench | 127.0.0.1 | UPDATE pgbench_tellers SET tbalance = tbalance + $1 WHERE tid = $2
pgbench | 127.0.0.1 | vacuum pgbench_tellers
pgbench | 127.0.0.1 | UPDATE pgbench_branches SET bbalance = bbalance + $1 WHERE bid = $2
pgbench | 127.0.0.1 | select o.n, p.partstrat, pg_catalog.count(i.inhparent) from pg_catalog.pg_class as c join pg_catalo
psql | 127.0.0.1 | SELECT application_name, client_ip, substr(query,$1,$2) as query FROM pg_stat_monitor
pgbench | 127.0.0.1 | select count(*) from pgbench_branches
(13 rows)
```
#### Error Messages / Error Codes and Error Level
**`elevel`**, **`sqlcode`**,**`message`**,: error level / sql code and  log/warning/ error message
### Query timing information
```sql
SELECT substr(query,0,50) AS query, decode_error_level(elevel) AS elevel,sqlcode, calls, substr(message,0,50) message
FROM pg_stat_monitor;
                       query                       | elevel | sqlcode | calls |                      message                      
---------------------------------------------------+--------+---------+-------+---------------------------------------------------
 select substr(query,$1,$2) as query, decode_error |        |       0 |     1 |
 select bucket,substr(query,$1,$2),decode_error_le |        |       0 |     3 |
                                                   | LOG    |       0 |     1 | database system is ready to accept connections
 select 1/0;                                       | ERROR  |     130 |     1 | division by zero
                                                   | LOG    |       0 |     1 | database system was shut down at 2020-11-11 11:37
 select $1/$2                                      |        |       0 |     1 |
(6 rows)
```
#### Query Timing Information
**`total_time`**,  **`min_time`**, **`max_time`**, **`mean_time`**: The total / minimum / maximum and mean time spent for the same query.
```
SELECT userid,  total_time, min_time, max_time, mean_time, query FROM pg_stat_monitor;
 userid |     total_time     |      min_time      |      max_time      |     mean_time      |                              query                              
SELECT userid, total_time, min_time, max_time, mean_time, query FROM pg_stat_monitor;
userid | total_time | min_time | max_time | mean_time | query
--------+--------------------+--------------------+--------------------+--------------------+------------------------------------------------------------------
     10 |               0.14 |               0.14 |               0.14 |               0.14 | select * from pg_stat_monitor_reset()
     10 |               0.19 |               0.19 |               0.19 |               0.19 | select userid,  dbid, queryid, query from pg_stat_monitor
     10 |               0.30 |               0.13 |               0.16 |               0.15 | select bucket, bucket_start_time, query from pg_stat_monitor
     10 |               0.29 |               0.29 |               0.29 |               0.29 | select userid,  dbid, queryid, query, calls from pg_stat_monitor
     10 |           11277.79 |           11277.79 |           11277.79 |           11277.79 | SELECT * FROM foo
10 | 0.14 | 0.14 | 0.14 | 0.14 | select * from pg_stat_monitor_reset()
10 | 0.19 | 0.19 | 0.19 | 0.19 | select userid, dbid, queryid, query from pg_stat_monitor
10 | 0.30 | 0.13 | 0.16 | 0.15 | select bucket, bucket_start_time, query from pg_stat_monitor
10 | 0.29 | 0.29 | 0.29 | 0.29 | select userid, dbid, queryid, query, calls from pg_stat_monitor
10 | 11277.79 | 11277.79 | 11277.79 |
```
#### Client IP address
**`client_ip`**: The IP address of the client that originated the query.
```sql
SELECT userid::regrole, datname, substr(query,0, 50) AS query, calls, client_ip
FROM pg_stat_monitor, pg_database
WHERE dbid = oid;
userid  | datname  |                       query                       | calls | client_ip
---------+----------+---------------------------------------------------+-------+-----------
 vagrant | postgres | UPDATE pgbench_branches SET bbalance = bbalance + |  1599 | 10.0.2.15
 vagrant | postgres | select userid::regrole, datname, substr(query,$1, |     5 | 10.0.2.15
 vagrant | postgres | UPDATE pgbench_accounts SET abalance = abalance + |  1599 | 10.0.2.15
 vagrant | postgres | select userid::regrole, datname, substr(query,$1, |     1 | 127.0.0.1
 vagrant | postgres | vacuum pgbench_tellers                            |     1 | 10.0.2.15
 vagrant | postgres | SELECT abalance FROM pgbench_accounts WHERE aid = |  1599 | 10.0.2.15
 vagrant | postgres | END                                               |  1599 | 10.0.2.15
 vagrant | postgres | select count(*) from pgbench_branches             |     1 | 10.0.2.15
 vagrant | postgres | BEGIN                                             |  1599 | 10.0.2.15
 vagrant | postgres | INSERT INTO pgbench_history (tid, bid, aid, delta |  1599 | 10.0.2.15
 vagrant | postgres | vacuum pgbench_branches                           |     1 | 10.0.2.15
 vagrant | postgres | truncate pgbench_history                          |     1 | 10.0.2.15
 vagrant | postgres | UPDATE pgbench_tellers SET tbalance = tbalance +  |  1599 | 10.0.2.15
```
#### Call Timings Histogram
**`resp_calls`**: Call histogram
```sql
SELECT resp_calls, query FROM pg_stat_monitor;
                    resp_calls                    |                 query                                        
--------------------------------------------------+----------------------------------------------
{1," 0"," 0"," 0"," 0"," 0"," 0"," 0"," 0"," 0"} | select client_ip, query from pg_stat_monitor
{3," 0"," 0"," 0"," 0"," 0"," 0"," 0"," 0"," 1"} | select * from pg_stat_monitor_reset()
{3," 0"," 0"," 0"," 0"," 0"," 0"," 0"," 0"," 1"} | SELECT * FROM foo
postgres=# SELECT * FROM histogram(0, 'F44CD1B4B33A47AF') AS a(range TEXT, freq INT, bar TEXT);
range | freq | bar
--------------------+------+--------------------------------
(0 - 3)} | 2 | ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
(3 - 10)} | 0 |
(10 - 31)} | 1 | ■■■■■■■■■■■■■■■
(31 - 100)} | 0 |
(100 - 316)} | 0 |
(316 - 1000)} | 0 |
(1000 - 3162)} | 0 |
(3162 - 10000)} | 0 |
(10000 - 31622)} | 0 |
(31622 - 100000)} | 0 |
(10 rows)
```
There are 10 timebase buckets of the time **`pg_stat_monitor.pgsm_respose_time_step`** in the field ``resp_calls``. The field's value shows how many queries run in that period of time.
#### Object Information.
**`relations`**: The list of tables involved in the query
##### Example 1: List all the table names involved in the query.
```sql
postgres=# SELECT relations,query FROM pg_stat_monitor;
relations | query
-------------------------------+------------------------------------------------------------------------------------------------------
| END
{pgbench_accounts} | SELECT abalance FROM pgbench_accounts WHERE aid = $1
| vacuum pgbench_branches
{pgbench_branches} | select count(*) from pgbench_branches
{pgbench_accounts} | UPDATE pgbench_accounts SET abalance = abalance + $1 WHERE aid = $2
| truncate pgbench_history
{pgbench_history} | INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES ($1, $2, $3, $4, CURRENT_TIMESTAMP)
{pg_stat_monitor,pg_database} | SELECT relations query FROM pg_stat_monitor
| vacuum pgbench_tellers
| BEGIN
{pgbench_tellers} | UPDATE pgbench_tellers SET tbalance = tbalance + $1 WHERE tid = $2
{pgbench_branches} | UPDATE pgbench_branches SET bbalance = bbalance + $1 WHERE bid = $2
(12 rows)
```
##### Example 2: List all the views and the name of the table in the view. Here we have a view "test_view"
```sql
\d+ test_view
                          View "public.test_view"
 Column |  Type   | Collation | Nullable | Default | Storage | Description
--------+---------+-----------+----------+---------+---------+-------------
 foo_a  | integer |           |          |         | plain   |
 bar_a  | integer |           |          |         | plain   |
View definition:
 SELECT f.a AS foo_a,
    b.a AS bar_a
   FROM foo f,
    bar b;
```
Now when we query ``pg_stat_monitor``, it will show the view name and also all the table names in the view.
```sql
SELECT relations, query FROM pg_stat_monitor;
      relations      |                                                query                                                
---------------------+------------------------------------------------------------------------------------------------------
 {test_view,foo,bar} | select * from test_view
 {foo,bar}           | select * from foo,bar
(2 rows)
```
#### Query command Type (SELECT, INSERT, UPDATE OR DELETE)
**`cmd_type`**: List the command type of the query.
```sql
postgres=# SELECT bucket, substr(query,0, 50) AS query, cmd_type FROM pg_stat_monitor WHERE elevel = 0;
bucket | query | cmd_type
--------+---------------------------------------------------+----------
4 | END |
4 | SELECT abalance FROM pgbench_accounts WHERE aid = | SELECT
4 | vacuum pgbench_branches |
4 | select count(*) from pgbench_branches | SELECT
4 | UPDATE pgbench_accounts SET abalance = abalance + | UPDATE
4 | truncate pgbench_history |
4 | INSERT INTO pgbench_history (tid, bid, aid, delta | INSERT
5 | SELECT relations query FROM pg_stat_monitor | SELECT
9 | SELECT bucket, substr(query,$1, $2) AS query, cmd |
4 | vacuum pgbench_tellers |
4 | BEGIN |
5 | SELECT relations,query FROM pg_stat_monitor | SELECT
4 | UPDATE pgbench_tellers SET tbalance = tbalance + | UPDATE
4 | UPDATE pgbench_branches SET bbalance = bbalance + | UPDATE
(14 rows)
```
#### Function Execution Tracking
**`top_queryid`**: Outer layer caller's query id.
```sql
CREATE OR REPLACE function add2(int, int) RETURNS int as
$$
BEGIN
return (select $1 + $2);
END;
$$ language plpgsql;
SELECT add2(1,2);
add2
-----
3
(1 row)
postgres=# SELECT queryid, top_queryid, query, top_query FROM pg_stat_monitor;
queryid | top_queryid | query. | top_query
------------------+------------------+-------------------------------------------------------------------------+-------------------
3408CA84B2353094 | | select add2($1,$2) |
762B99349F6C7F31 | 3408CA84B2353094 | SELECT (select $1 + $2) | select add2($1,$2)
(2 rows)
```
#### Monitor Query Execution Plan.
### Query execution plan
```sql
postgres=# SELECT substr(query,0,50), query_plan from pg_stat_monitor limit 10;
@ -445,9 +433,38 @@ postgres=# SELECT substr(query,0,50), query_plan from pg_stat_monitor limit 10;
vacuum pgbench_tellers |
UPDATE pgbench_accounts SET abalance = abalance + |
(10 rows)
```
#### SQL Commenter / tags.
The `plan` column does not contain costing, width and other values. This is an expected behavior as each row is an accumulation of statistics based on `plan` and amongst other key columns. Plan is only available when the `pgsm_enable_query_plan` configuration parameter is enabled.
### Query type filtering
``pg_stat_monitor`` monitors queries per type (SELECT, INSERT, UPDATE OR DELETE) and classifies them accordingly in the ``cmd_type`` column thus reducing your efforts.
```sql
postgres=# SELECT bucket, substr(query,0, 50) AS query, cmd_type FROM pg_stat_monitor WHERE elevel = 0;
bucket | query | cmd_type
--------+---------------------------------------------------+----------
4 | END |
4 | SELECT abalance FROM pgbench_accounts WHERE aid = | SELECT
4 | vacuum pgbench_branches |
4 | select count(*) from pgbench_branches | SELECT
4 | UPDATE pgbench_accounts SET abalance = abalance + | UPDATE
4 | truncate pgbench_history |
4 | INSERT INTO pgbench_history (tid, bid, aid, delta | INSERT
5 | SELECT relations query FROM pg_stat_monitor | SELECT
9 | SELECT bucket, substr(query,$1, $2) AS query, cmd |
4 | vacuum pgbench_tellers |
4 | BEGIN |
5 | SELECT relations,query FROM pg_stat_monitor | SELECT
4 | UPDATE pgbench_tellers SET tbalance = tbalance + | UPDATE
4 | UPDATE pgbench_branches SET bbalance = bbalance + | UPDATE
(14 rows)
```
### Query metadata
The `comments` column contains any text wrapped in `“/*”` and `“*/”` comment tags. The `pg_stat_monitor` extension picks up these comments and makes them available in the comments column. Please note that only the latest comment value is preserved per row. The comments may be put in any format that can be parsed by a tool.
```sql
CREATE EXTENSION hstore;
@ -524,3 +541,131 @@ postgres=# select query, text_to_hstore(comments)->'real_ip' as real_ip from pg_
SELECT $1 AS num1,$2 AS num2, $3 AS num3, $4 AS num4 /* { "application", psql_app, "real_ip", 192.168.1.3} */ | 192.168.1.3
(10 rows)
```
### Top query tracking
In the following example we create a function `add2` that adds one parameter value to another one and call this function to calculate 1+2.
```sql
CREATE OR REPLACE function add2(int, int) RETURNS int as
$$
BEGIN
return (select $1 + $2);
END;
$$ language plpgsql;
SELECT add2(1,2);
add2
-----
3
(1 row)
The ``pg_stat_monitor`` view shows all executed queries and shows the very first query in a row - calling the `add2` function.
postgres=# SELECT queryid, top_queryid, query, top_query FROM pg_stat_monitor;
queryid | top_queryid | query. | top_query
------------------+------------------+-------------------------------------------------------------------------+-------------------
3408CA84B2353094 | | select add2($1,$2) |
762B99349F6C7F31 | 3408CA84B2353094 | SELECT (select $1 + $2) | select add2($1,$2)
(2 rows)
```
### Relations
**Example 1: List all the table names involved in the query.**
```sql
postgres=# SELECT relations,query FROM pg_stat_monitor;
relations | query
-------------------------------+------------------------------------------------------------------------------------------------------
| END
{public.pgbench_accounts} | SELECT abalance FROM pgbench_accounts WHERE aid = $1
| vacuum pgbench_branches
{public.pgbench_branches} | select count(*) from pgbench_branches
{public.pgbench_accounts} | UPDATE pgbench_accounts SET abalance = abalance + $1 WHERE aid = $2
| truncate pgbench_history
{public.pgbench_history} | INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES ($1, $2, $3, $4, CURRENT_TIMESTAMP)
{public.pg_stat_monitor,pg_catalog.pg_database} | SELECT relations query FROM pg_stat_monitor
| vacuum pgbench_tellers
| BEGIN
{public.pgbench_tellers} | UPDATE pgbench_tellers SET tbalance = tbalance + $1 WHERE tid = $2
{public.pgbench_branches} | UPDATE pgbench_branches SET bbalance = bbalance + $1 WHERE bid = $2
(12 rows)
```
**Example 2: List all the views and the name of the table in the view. Here we have a view "test_view"**
```sql
\d+ test_view
View "public.test_view"
Column | Type | Collation | Nullable | Default | Storage | Description
--------+---------+-----------+----------+---------+---------+-------------
foo_a | integer | | | | plain |
bar_a | integer | | | | plain |
View definition:
SELECT f.a AS foo_a,
b.a AS bar_a
FROM foo f,
bar b;
```
Now when we query the ``pg_stat_monitor``, it will show the view name and also all the table names in the view. Note that the view name is followed by an asterisk (*).
```sql
SELECT relations, query FROM pg_stat_monitor;
relations | query
---------------------+----------------------------------------------------
{test_view*,foo,bar} | select * from test_view
{foo,bar} | select * from foo,bar
(2 rows)
```
### Queries terminated with errors
```sql
SELECT substr(query,0,50) AS query, decode_error_level(elevel) AS elevel,sqlcode, calls, substr(message,0,50) message
FROM pg_stat_monitor;
query | elevel | sqlcode | calls | message
---------------------------------------------------+--------+---------+-------+---------------------------------------------------
select substr(query,$1,$2) as query, decode_error | | 0 | 1 |
select bucket,substr(query,$1,$2),decode_error_le | | 0 | 3 |
| LOG | 0 | 1 | database system is ready to accept connections
select 1/0; | ERROR | 130 | 1 | division by zero
| LOG | 0 | 1 | database system was shut down at 2020-11-11 11:37
select $1/$2 | | 0 | 1 |
(6 rows)
11277.79 | SELECT * FROM foo
```
### Histogram
Histogram (the `resp_calls` parameter) provides a visual representation of query performance. With the help of the histogram function, you can view a timing/calling data histogram in response to a SQL query.
```sql
SELECT resp_calls, query FROM pg_stat_monitor;
                    resp_calls                    |                 query                                        
--------------------------------------------------+----------------------------------------------
{1," 0"," 0"," 0"," 0"," 0"," 0"," 0"," 0"," 0"} | select client_ip, query from pg_stat_monitor
{3," 0"," 0"," 0"," 0"," 0"," 0"," 0"," 0"," 1"} | select * from pg_stat_monitor_reset()
{3," 0"," 0"," 0"," 0"," 0"," 0"," 0"," 0"," 1"} | SELECT * FROM foo
postgres=# SELECT * FROM histogram(0, 'F44CD1B4B33A47AF') AS a(range TEXT, freq INT, bar TEXT);
range | freq | bar
--------------------+------+--------------------------------
(0 - 3)} | 2 | ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
(3 - 10)} | 0 |
(10 - 31)} | 1 | ■■■■■■■■■■■■■■■
(31 - 100)} | 0 |
(100 - 316)} | 0 |
(316 - 1000)} | 0 |
(1000 - 3162)} | 0 |
(3162 - 10000)} | 0 |
(10000 - 31622)} | 0 |
(31622 - 100000)} | 0 |
(10 rows)
```
There are 10 time based buckets of the time generated automatically based on total buckets in the field ``resp_calls``. The value in the field shows how many queries run in that period of time.

62
guc.c
View File

@ -21,6 +21,7 @@
GucVariable conf[MAX_SETTINGS];
static void DefineIntGUC(GucVariable *conf);
static void DefineBoolGUC(GucVariable *conf);
static void DefineEnumGUC(GucVariable *conf, const struct config_enum_entry *options);
/*
* Define (or redefine) custom GUC variables.
@ -29,6 +30,7 @@ void
init_guc(void)
{
int i = 0;
conf[i] = (GucVariable) {
.guc_name = "pg_stat_monitor.pgsm_max",
.guc_desc = "Sets the maximum size of shared memory in (MB) used for statement's metadata tracked by pg_stat_monitor.",
@ -53,18 +55,6 @@ init_guc(void)
};
DefineIntGUC(&conf[i++]);
conf[i] = (GucVariable) {
.guc_name = "pg_stat_monitor.pgsm_enable",
.guc_desc = "Enable/Disable statistics collector.",
.guc_default = 1,
.guc_min = 0,
.guc_max = 0,
.guc_restart = false,
.guc_unit = 0,
.guc_value = &PGSM_ENABLED
};
DefineBoolGUC(&conf[i++]);
conf[i] = (GucVariable) {
.guc_name = "pg_stat_monitor.pgsm_track_utility",
.guc_desc = "Selects whether utility commands are tracked.",
@ -185,12 +175,39 @@ init_guc(void)
};
DefineBoolGUC(&conf[i++]);
conf[i] = (GucVariable) {
.guc_name = "pg_stat_monitor.track",
.guc_desc = "Selects which statements are tracked by pg_stat_monitor.",
.n_options = 3,
.guc_default = PGSM_TRACK_TOP,
.guc_min = PSGM_TRACK_NONE,
.guc_max = PGSM_TRACK_ALL,
.guc_restart = false,
.guc_unit = 0,
.guc_value = &PGSM_TRACK
};
for (int j = 0; j < conf[i].n_options; ++j) {
strlcpy(conf[i].guc_options[j], track_options[j].name, sizeof(conf[i].guc_options[j]));
}
DefineEnumGUC(&conf[i++], track_options);
conf[i] = (GucVariable) {
.guc_name = "pg_stat_monitor.extract_comments",
.guc_desc = "Enable/Disable extracting comments from queries.",
.guc_default = 0,
.guc_min = 0,
.guc_max = 0,
.guc_restart = false,
.guc_unit = 0,
.guc_value = &PGSM_EXTRACT_COMMENTS
};
DefineBoolGUC(&conf[i++]);
#if PG_VERSION_NUM >= 130000
conf[i] = (GucVariable) {
.guc_name = "pg_stat_monitor.pgsm_track_planning",
.guc_desc = "Selects whether planning statistics are tracked.",
.guc_default = 1,
.guc_default = 0,
.guc_min = 0,
.guc_max = 0,
.guc_restart = false,
@ -204,6 +221,7 @@ init_guc(void)
static void
DefineIntGUC(GucVariable *conf)
{
conf->type = PGC_INT;
DefineCustomIntVariable(conf->guc_name,
conf->guc_desc,
NULL,
@ -220,6 +238,7 @@ DefineIntGUC(GucVariable *conf)
static void
DefineBoolGUC(GucVariable *conf)
{
conf->type = PGC_BOOL;
DefineCustomBoolVariable(conf->guc_name,
conf->guc_desc,
NULL,
@ -232,6 +251,23 @@ DefineBoolGUC(GucVariable *conf)
NULL);
}
static void
DefineEnumGUC(GucVariable *conf, const struct config_enum_entry *options)
{
conf->type = PGC_ENUM;
DefineCustomEnumVariable(conf->guc_name,
conf->guc_desc,
NULL,
conf->guc_value,
PGSM_TRACK_TOP,
options,
conf->guc_restart ? PGC_POSTMASTER : PGC_USERSET,
0,
NULL,
NULL,
NULL);
}
GucVariable*
get_conf(int i)
{

View File

@ -17,24 +17,14 @@
#include "postgres.h"
#include "nodes/pg_list.h"
#include "pgsm_errors.h"
#include "pg_stat_monitor.h"
static pgssSharedState *pgss;
static HTAB *pgss_hash;
static HTAB *pgss_query_hash;
static HTAB* hash_init(const char *hash_name, int key_size, int entry_size, int hash_size);
/*
* Copy query from src_buffer to dst_buff.
* Use query_id and query_pos to fast locate query in source buffer.
* Store updated query position in the destination buffer into param query_pos.
*/
static bool copy_query(uint64 bucket_id,
uint64 query_id,
uint64 query_pos,
unsigned char *dst_buf,
unsigned char *src_buf,
size_t *new_query_pos);
static HTAB*
hash_init(const char *hash_name, int key_size, int entry_size, int hash_size)
@ -50,7 +40,6 @@ void
pgss_startup(void)
{
bool found = false;
int32 i;
/* reset in case this is a restart within the postmaster */
@ -66,7 +55,9 @@ pgss_startup(void)
if (!found)
{
/* First time through ... */
pgss->lock = &(GetNamedLWLockTranche("pg_stat_monitor"))->lock;
LWLockPadded *pgsm_locks = GetNamedLWLockTranche("pg_stat_monitor");
pgss->lock = &(pgsm_locks[0].lock);
pgss->errors_lock = &(pgsm_locks[1].lock);
SpinLockInit(&pgss->mutex);
ResetSharedState(pgss);
}
@ -75,16 +66,12 @@ pgss_startup(void)
init_hook_stats();
#endif
pgss->query_buf_size_bucket = MAX_QUERY_BUF / PGSM_MAX_BUCKETS;
for (i = 0; i < PGSM_MAX_BUCKETS; i++)
{
unsigned char *buf = (unsigned char *)ShmemAlloc(pgss->query_buf_size_bucket);
set_qbuf(i, buf);
memset(buf, 0, sizeof (uint64));
}
set_qbuf((unsigned char *)ShmemAlloc(MAX_QUERY_BUF));
pgss_hash = hash_init("pg_stat_monitor: bucket hashtable", sizeof(pgssHashKey), sizeof(pgssEntry), MAX_BUCKET_ENTRIES);
pgss_query_hash = hash_init("pg_stat_monitor: queryID hashtable", sizeof(uint64), sizeof(pgssQueryEntry), MAX_BUCKET_ENTRIES);
psgm_errors_init();
LWLockRelease(AddinShmemInitLock);
@ -107,6 +94,12 @@ pgsm_get_hash(void)
return pgss_hash;
}
HTAB*
pgsm_get_query_hash(void)
{
return pgss_query_hash;
}
/*
* shmem_shutdown hook: Dump statistics into file.
*
@ -144,15 +137,21 @@ hash_entry_alloc(pgssSharedState *pgss, pgssHashKey *key, int encoding)
{
pgssEntry *entry = NULL;
bool found = false;
long bucket_entries_cnt;
if (hash_get_num_entries(pgss_hash) >= MAX_BUCKET_ENTRIES)
bucket_entries_cnt = hash_get_num_entries(pgss_hash);
if (bucket_entries_cnt >= MAX_BUCKET_ENTRIES)
{
elog(DEBUG1, "%s", "pg_stat_monitor: out of memory");
pgsm_log_error("hash_entry_alloc: BUCKET OVERFLOW. entries(%d) >= max_entries(%d)",
bucket_entries_cnt, MAX_BUCKET_ENTRIES);
return NULL;
}
/* Find or create an entry with desired hash code */
entry = (pgssEntry *) hash_search(pgss_hash, key, HASH_ENTER_NULL, &found);
if (!found)
if (entry == NULL)
pgsm_log_error("hash_entry_alloc: OUT OF MEMORY");
else if (!found)
{
pgss->bucket_entry[pg_atomic_read_u64(&pgss->current_wbucket)]++;
/* New entry, initialize it */
@ -164,8 +163,7 @@ hash_entry_alloc(pgssSharedState *pgss, pgssHashKey *key, int encoding)
/* ... and don't forget the query text metadata */
entry->encoding = encoding;
}
if (entry == NULL)
elog(DEBUG1, "%s", "pg_stat_monitor: out of memory");
return entry;
}
@ -182,22 +180,15 @@ hash_entry_alloc(pgssSharedState *pgss, pgssHashKey *key, int encoding)
* Caller must hold an exclusive lock on pgss->lock.
*/
void
hash_entry_dealloc(int new_bucket_id, int old_bucket_id, unsigned char *query_buffer[])
hash_entry_dealloc(int new_bucket_id, int old_bucket_id, unsigned char *query_buffer)
{
HASH_SEQ_STATUS hash_seq;
pgssEntry *entry = NULL;
pgssSharedState *pgss = pgsm_get_ss();
/* Store pending query ids from the previous bucket. */
List *pending_entries = NIL;
ListCell *pending_entry;
if (new_bucket_id != -1)
{
/* Clear all queries in the query buffer for the new bucket. */
memset(query_buffer[new_bucket_id], 0, pgss->query_buf_size_bucket);
}
/* Iterate over the hash table. */
hash_seq_init(&hash_seq, pgss_hash);
while ((entry = hash_seq_search(&hash_seq)) != NULL)
@ -210,6 +201,11 @@ hash_entry_dealloc(int new_bucket_id, int old_bucket_id, unsigned char *query_bu
(entry->key.bucket_id == new_bucket_id &&
(entry->counters.state == PGSS_FINISHED || entry->counters.state == PGSS_ERROR)))
{
if (new_bucket_id == -1) {
/* pg_stat_monitor_reset(), remove entry from query hash table too. */
hash_search(pgss_query_hash, &(entry->key.queryid), HASH_REMOVE, NULL);
}
entry = hash_search(pgss_hash, &entry->key, HASH_REMOVE, NULL);
}
@ -229,8 +225,20 @@ hash_entry_dealloc(int new_bucket_id, int old_bucket_id, unsigned char *query_bu
pgssEntry *bkp_entry = malloc(sizeof(pgssEntry));
if (!bkp_entry)
{
/* No memory, remove pending query entry from the previous bucket. */
elog(ERROR, "hash_entry_dealloc: out of memory");
pgsm_log_error("hash_entry_dealloc: out of memory");
/*
* No memory, If the entry has calls > 1 then we change the state to finished,
* as the pending query will likely finish execution during the new bucket
* time window. The pending query will vanish in this case, can't list it
* until it completes.
*
* If there is only one call to the query and it's pending, remove the
* entry from the previous bucket and allow it to finish in the new bucket,
* in order to avoid the query living in the old bucket forever.
*/
if (entry->counters.calls.calls > 1)
entry->counters.state = PGSS_FINISHED;
else
entry = hash_search(pgss_hash, &entry->key, HASH_REMOVE, NULL);
continue;
}
@ -244,7 +252,19 @@ hash_entry_dealloc(int new_bucket_id, int old_bucket_id, unsigned char *query_bu
/* Add the entry to a list of nodes to be processed later. */
pending_entries = lappend(pending_entries, bkp_entry);
/* Finally remove the pending query from the expired bucket id. */
/*
* If the entry has calls > 1 then we change the state to finished in
* the previous bucket, as the pending query will likely finish execution
* during the new bucket time window. Can't remove it from the previous bucket
* as it may have many calls and we would lose the query statistics.
*
* If there is only one call to the query and it's pending, remove the entry
* from the previous bucket and allow it to finish in the new bucket,
* in order to avoid the query living in the old bucket forever.
*/
if (entry->counters.calls.calls > 1)
entry->counters.state = PGSS_FINISHED;
else
entry = hash_search(pgss_hash, &entry->key, HASH_REMOVE, NULL);
}
}
@ -261,20 +281,14 @@ hash_entry_dealloc(int new_bucket_id, int old_bucket_id, unsigned char *query_bu
new_entry = (pgssEntry *) hash_search(pgss_hash, &old_entry->key, HASH_ENTER_NULL, &found);
if (new_entry == NULL)
elog(DEBUG1, "%s", "pg_stat_monitor: out of memory");
pgsm_log_error("hash_entry_dealloc: out of memory");
else if (!found)
{
/* Restore counters and other data. */
new_entry->counters = old_entry->counters;
SpinLockInit(&new_entry->mutex);
new_entry->encoding = old_entry->encoding;
/* copy query's text from previous bucket to the new one. */
copy_query(new_bucket_id,
new_entry->key.queryid, /* query id */
old_entry->query_pos, /* query position in buffer */
query_buffer[new_bucket_id], /* destination query buffer */
query_buffer[old_bucket_id], /* source query buffer */
&new_entry->query_pos); /* position in which query was inserted into destination buffer */
new_entry->query_pos = old_entry->query_pos;
}
free(old_entry);
@ -310,39 +324,3 @@ IsHashInitialize(void)
return (pgss != NULL &&
pgss_hash != NULL);
}
static bool copy_query(uint64 bucket_id,
uint64 query_id,
uint64 query_pos,
unsigned char *dst_buf,
unsigned char *src_buf,
size_t *new_query_pos)
{
uint64 query_len = 0;
uint64 buf_len = 0;
memcpy(&buf_len, src_buf, sizeof (uint64));
if (buf_len <= 0)
return false;
/* Try to locate the query directly. */
if (query_pos != 0 && (query_pos + sizeof(uint64) + sizeof(uint64)) < buf_len)
{
if (*(uint64 *)&src_buf[query_pos] != query_id)
return false;
query_pos += sizeof(uint64);
memcpy(&query_len, &src_buf[query_pos], sizeof(uint64)); /* query len */
query_pos += sizeof(uint64);
if (query_pos + query_len > buf_len) /* avoid reading past buffer's length. */
return false;
return SaveQueryText(bucket_id, query_id, dst_buf,
(const char *)&src_buf[query_pos],
query_len, new_query_pos);
}
return false;
}

View File

@ -12,18 +12,17 @@ export USE_PGXS=1
dh $@
override_dh_auto_build:
+pg_buildext build build-%v
+pg_buildext clean . build-%v
+pg_buildext build . build-%v
override_dh_auto_test:
# nothing to do here
override_dh_auto_install:
+pg_buildext install build-%v percona-pg-stat-monitor@@PG_REL@@
+pg_buildext install . build-%v percona-pg-stat-monitor@@PG_REL@@
override_dh_installdocs:
dh_installdocs --all README.*
override_dh_auto_clean:
+pg_buildext clean build-%v
rm -rf results regression.*
mkdir results
rm -rf results/* regression.*

View File

@ -1,4 +1,4 @@
%global sname percona-pg-stat-monitor
%global sname percona-pg_stat_monitor
%global pgrel @@PG_REL@@
%global rpm_release @@RPM_RELEASE@@
%global pginstdir /usr/pgsql-@@PG_REL@@/
@ -8,12 +8,16 @@ Name: %{sname}%{pgrel}
Version: @@VERSION@@
Release: %{rpm_release}%{?dist}
License: PostgreSQL
Source0: %{sname}%{pgrel}-%{version}.tar.gz
Source0: percona-pg-stat-monitor%{pgrel}-%{version}.tar.gz
URL: https://github.com/Percona-Lab/pg_stat_monitor
BuildRequires: percona-postgresql%{pgrel}-devel
Requires: postgresql-server
Provides: percona-pg-stat-monitor%{pgrel}
Conflicts: percona-pg-stat-monitor%{pgrel}
Obsoletes: percona-pg-stat-monitor%{pgrel}
Epoch: 1
Packager: Percona Development Team <https://jira.percona.com>
Vendor: Percona, Inc
%description
The pg_stat_monitor is statistics collector tool
@ -25,7 +29,7 @@ It provides all the features of pg_stat_statment plus its own feature set.
%prep
%setup -q -n %{sname}%{pgrel}-%{version}
%setup -q -n percona-pg-stat-monitor%{pgrel}-%{version}
%build
@ -61,5 +65,5 @@ sed -i 's:PG_CONFIG = pg_config:PG_CONFIG = /usr/pgsql-%{pgrel}/bin/pg_config:'
%changelog
* Thu Dec 19 2019 Oleksandr Miroshnychenko <alex.miroshnychenko@percona.com> - 1.0.0-1
* Wed Nov 17 2021 Evgeniy Patlan <evgeniy.patlan@percona.com> - 1.0.0-1
- Initial build

View File

@ -333,10 +333,10 @@ build_rpm(){
echo "It is not possible to build rpm here"
exit 1
fi
SRC_RPM=$(basename $(find $WORKDIR/srpm -name 'percona-pg-stat-monitor*.src.rpm' | sort | tail -n1))
SRC_RPM=$(basename $(find $WORKDIR/srpm -name 'percona-pg_stat_monitor*.src.rpm' | sort | tail -n1))
if [ -z $SRC_RPM ]
then
SRC_RPM=$(basename $(find $CURDIR/srpm -name 'percona-pg-stat-monitor*.src.rpm' | sort | tail -n1))
SRC_RPM=$(basename $(find $CURDIR/srpm -name 'percona-pg_stat_monitor*.src.rpm' | sort | tail -n1))
if [ -z $SRC_RPM ]
then
echo "There is no src rpm for build"

View File

@ -47,19 +47,22 @@ CREATE FUNCTION pg_stat_monitor_internal(IN showtext boolean,
OUT bucket_start_time text,
OUT calls int8, -- 16
OUT total_time float8,
OUT min_time float8,
OUT max_time float8,
OUT mean_time float8,
OUT stddev_time float8,
OUT total_exec_time float8,
OUT min_exec_time float8,
OUT max_exec_time float8,
OUT mean_exec_time float8,
OUT stddev_exec_time float8,
OUT rows_retrieved int8,
OUT plans_calls int8, -- 23
OUT plan_total_time float8,
OUT plan_min_time float8,
OUT plan_max_time float8,
OUT plan_mean_time float8,
OUT plan_stddev_time float8,
OUT total_plan_time float8,
OUT min_plan_time float8,
OUT max_plan_time float8,
OUT mean_plan_time float8,
OUT stddev_plan_time float8,
OUT shared_blks_hit int8, -- 29
OUT shared_blks_read int8,
@ -79,7 +82,8 @@ CREATE FUNCTION pg_stat_monitor_internal(IN showtext boolean,
OUT wal_records int8,
OUT wal_fpi int8,
OUT wal_bytes numeric,
OUT comments TEXT
OUT comments TEXT,
OUT toplevel BOOLEAN
)
RETURNS SETOF record
AS 'MODULE_PATHNAME', 'pg_stat_monitor'
@ -158,17 +162,21 @@ CREATE VIEW pg_stat_monitor AS SELECT
sqlcode,
message,
calls,
total_time,
min_time,
max_time,
mean_time,
stddev_time,
total_exec_time,
min_exec_time,
max_exec_time,
mean_exec_time,
stddev_exec_time,
rows_retrieved,
plans_calls,
plan_total_time,
plan_min_time,
plan_max_time,
plan_mean_time,
total_plan_time,
min_plan_time,
max_plan_time,
mean_plan_time,
stddev_plan_time,
shared_blks_hit,
shared_blks_read,
@ -228,26 +236,26 @@ end loop;
END
$$ language plpgsql;
CREATE FUNCTION pg_stat_monitor_hook_stats(
OUT hook text,
OUT min_time float8,
OUT max_time float8,
OUT total_time float8,
OUT ncalls int8
)
RETURNS SETOF record
AS 'MODULE_PATHNAME', 'pg_stat_monitor_hook_stats'
LANGUAGE C STRICT VOLATILE PARALLEL SAFE;
--CREATE FUNCTION pg_stat_monitor_hook_stats(
-- OUT hook text,
-- OUT min_time float8,
-- OUT max_time float8,
-- OUT total_time float8,
-- OUT ncalls int8
--)
--RETURNS SETOF record
--AS 'MODULE_PATHNAME', 'pg_stat_monitor_hook_stats'
--LANGUAGE C STRICT VOLATILE PARALLEL SAFE;
CREATE VIEW pg_stat_monitor_hook_stats AS SELECT
hook,
min_time,
max_time,
total_time,
total_time / greatest(ncalls, 1) as avg_time,
ncalls,
ROUND(CAST(total_time / greatest(sum(total_time) OVER(), 0.00000001) * 100 as numeric), 2)::text || '%' as load_comparison
FROM pg_stat_monitor_hook_stats();
--CREATE VIEW pg_stat_monitor_hook_stats AS SELECT
-- hook,
-- min_time,
-- max_time,
-- total_time,
-- total_time / greatest(ncalls, 1) as avg_time,
-- ncalls,
-- ROUND(CAST(total_time / greatest(sum(total_time) OVER(), 0.00000001) * 100 as numeric), 2)::text || '%' as load_comparison
--FROM pg_stat_monitor_hook_stats();
GRANT SELECT ON pg_stat_monitor TO PUBLIC;
GRANT SELECT ON pg_stat_monitor_settings TO PUBLIC;

View File

@ -0,0 +1,264 @@
/* contrib/pg_stat_monitor/pg_stat_monitor--1.1.sql */
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "CREATE EXTENSION pg_stat_monitor" to load this file. \quit
-- Register functions.
CREATE FUNCTION pg_stat_monitor_reset()
RETURNS void
AS 'MODULE_PATHNAME'
LANGUAGE C PARALLEL SAFE;
CREATE FUNCTION pg_stat_monitor_version()
RETURNS text
AS 'MODULE_PATHNAME'
LANGUAGE C PARALLEL SAFE;
CREATE FUNCTION get_histogram_timings()
RETURNS text
AS 'MODULE_PATHNAME'
LANGUAGE C PARALLEL SAFE;
CREATE FUNCTION range()
RETURNS text[] AS $$
SELECT string_to_array(get_histogram_timings(), ',');
$$ LANGUAGE SQL;
CREATE FUNCTION pg_stat_monitor_internal(IN showtext boolean,
OUT bucket int8, -- 0
OUT userid oid,
OUT dbid oid,
OUT client_ip int8,
OUT queryid text, -- 4
OUT planid text,
OUT query text,
OUT query_plan text,
OUT state_code int8,
OUT top_queryid text,
OUT top_query text,
OUT application_name text,
OUT relations text, -- 11
OUT cmd_type int,
OUT elevel int,
OUT sqlcode TEXT,
OUT message text,
OUT bucket_start_time text,
OUT calls int8, -- 16
OUT total_exec_time float8,
OUT min_exec_time float8,
OUT max_exec_time float8,
OUT mean_exec_time float8,
OUT stddev_exec_time float8,
OUT rows_retrieved int8,
OUT plans_calls int8, -- 23
OUT total_plan_time float8,
OUT min_plan_time float8,
OUT max_plan_time float8,
OUT mean_plan_time float8,
OUT stddev_plan_time float8,
OUT shared_blks_hit int8, -- 29
OUT shared_blks_read int8,
OUT shared_blks_dirtied int8,
OUT shared_blks_written int8,
OUT local_blks_hit int8,
OUT local_blks_read int8,
OUT local_blks_dirtied int8,
OUT local_blks_written int8,
OUT temp_blks_read int8,
OUT temp_blks_written int8,
OUT blk_read_time float8,
OUT blk_write_time float8,
OUT resp_calls text, -- 41
OUT cpu_user_time float8,
OUT cpu_sys_time float8,
OUT wal_records int8,
OUT wal_fpi int8,
OUT wal_bytes numeric,
OUT comments TEXT,
OUT toplevel BOOLEAN
)
RETURNS SETOF record
AS 'MODULE_PATHNAME', 'pg_stat_monitor'
LANGUAGE C STRICT VOLATILE PARALLEL SAFE;
CREATE OR REPLACE FUNCTION get_state(state_code int8) RETURNS TEXT AS
$$
SELECT
CASE
WHEN state_code = 0 THEN 'PARSING'
WHEN state_code = 1 THEN 'PLANNING'
WHEN state_code = 2 THEN 'ACTIVE'
WHEN state_code = 3 THEN 'FINISHED'
WHEN state_code = 4 THEN 'FINISHED WITH ERROR'
END
$$
LANGUAGE SQL PARALLEL SAFE;
CREATE or REPLACE FUNCTION get_cmd_type (cmd_type INTEGER) RETURNS TEXT AS
$$
SELECT
CASE
WHEN cmd_type = 0 THEN ''
WHEN cmd_type = 1 THEN 'SELECT'
WHEN cmd_type = 2 THEN 'UPDATE'
WHEN cmd_type = 3 THEN 'INSERT'
WHEN cmd_type = 4 THEN 'DELETE'
WHEN cmd_type = 5 THEN 'UTILITY'
WHEN cmd_type = 6 THEN 'NOTHING'
END
$$
LANGUAGE SQL PARALLEL SAFE;
CREATE FUNCTION pg_stat_monitor_settings(
OUT name text,
OUT value INTEGER,
OUT default_value INTEGER,
OUT description text,
OUT minimum INTEGER,
OUT maximum INTEGER,
OUT restart INTEGER
)
RETURNS SETOF record
AS 'MODULE_PATHNAME', 'pg_stat_monitor_settings'
LANGUAGE C STRICT VOLATILE PARALLEL SAFE;
CREATE VIEW pg_stat_monitor_settings AS SELECT
name,
value,
default_value,
description,
minimum,
maximum,
restart
FROM pg_stat_monitor_settings();
-- Register a view on the function for ease of use.
CREATE VIEW pg_stat_monitor AS SELECT
bucket,
bucket_start_time AS bucket_start_time,
userid::regrole,
datname,
'0.0.0.0'::inet + client_ip AS client_ip,
queryid,
toplevel,
top_queryid,
query,
comments,
planid,
query_plan,
top_query,
application_name,
string_to_array(relations, ',') AS relations,
cmd_type,
get_cmd_type(cmd_type) AS cmd_type_text,
elevel,
sqlcode,
message,
calls,
total_exec_time,
min_exec_time,
max_exec_time,
mean_exec_time,
stddev_exec_time,
rows_retrieved,
plans_calls,
total_plan_time,
min_plan_time,
max_plan_time,
mean_plan_time,
stddev_plan_time,
shared_blks_hit,
shared_blks_read,
shared_blks_dirtied,
shared_blks_written,
local_blks_hit,
local_blks_read,
local_blks_dirtied,
local_blks_written,
temp_blks_read,
temp_blks_written,
blk_read_time,
blk_write_time,
(string_to_array(resp_calls, ',')) resp_calls,
cpu_user_time,
cpu_sys_time,
wal_records,
wal_fpi,
wal_bytes,
state_code,
get_state(state_code) as state
FROM pg_stat_monitor_internal(TRUE) p, pg_database d WHERE dbid = oid
ORDER BY bucket_start_time;
CREATE FUNCTION decode_error_level(elevel int)
RETURNS text
AS
$$
SELECT
CASE
WHEN elevel = 0 THEN ''
WHEN elevel = 10 THEN 'DEBUG5'
WHEN elevel = 11 THEN 'DEBUG4'
WHEN elevel = 12 THEN 'DEBUG3'
WHEN elevel = 13 THEN 'DEBUG2'
WHEN elevel = 14 THEN 'DEBUG1'
WHEN elevel = 15 THEN 'LOG'
WHEN elevel = 16 THEN 'LOG_SERVER_ONLY'
WHEN elevel = 17 THEN 'INFO'
WHEN elevel = 18 THEN 'NOTICE'
WHEN elevel = 19 THEN 'WARNING'
WHEN elevel = 20 THEN 'ERROR'
END
$$
LANGUAGE SQL PARALLEL SAFE;
CREATE OR REPLACE FUNCTION histogram(_bucket int, _quryid text)
RETURNS SETOF RECORD AS $$
DECLARE
rec record;
BEGIN
for rec in
with stat as (select queryid, bucket, unnest(range()) as range, unnest(resp_calls)::int freq from pg_stat_monitor) select range, freq, repeat('', (freq::float / max(freq) over() * 30)::int) as bar from stat where queryid = _quryid and bucket = _bucket
loop
return next rec;
end loop;
END
$$ language plpgsql;
--CREATE FUNCTION pg_stat_monitor_hook_stats(
-- OUT hook text,
-- OUT min_time float8,
-- OUT max_time float8,
-- OUT total_time float8,
-- OUT ncalls int8
--)
--RETURNS SETOF record
--AS 'MODULE_PATHNAME', 'pg_stat_monitor_hook_stats'
--LANGUAGE C STRICT VOLATILE PARALLEL SAFE;
--
--CREATE VIEW pg_stat_monitor_hook_stats AS SELECT
-- hook,
-- min_time,
-- max_time,
-- total_time,
-- total_time / greatest(ncalls, 1) as avg_time,
-- ncalls,
-- ROUND(CAST(total_time / greatest(sum(total_time) OVER(), 0.00000001) * 100 as numeric), 2)::text || '%' as load_comparison
--FROM pg_stat_monitor_hook_stats();
GRANT SELECT ON pg_stat_monitor TO PUBLIC;
GRANT SELECT ON pg_stat_monitor_settings TO PUBLIC;
-- Don't want this to be available to non-superusers.
REVOKE ALL ON FUNCTION pg_stat_monitor_reset() FROM PUBLIC;

285
pg_stat_monitor--1.0.sql.in Normal file
View File

@ -0,0 +1,285 @@
/* contrib/pg_stat_monitor/pg_stat_monitor--1.1.sql */
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "CREATE EXTENSION pg_stat_monitor" to load this file. \quit
-- Register functions.
CREATE FUNCTION pg_stat_monitor_reset()
RETURNS void
AS 'MODULE_PATHNAME'
LANGUAGE C PARALLEL SAFE;
CREATE FUNCTION pg_stat_monitor_version()
RETURNS text
AS 'MODULE_PATHNAME'
LANGUAGE C PARALLEL SAFE;
CREATE FUNCTION get_histogram_timings()
RETURNS text
AS 'MODULE_PATHNAME'
LANGUAGE C PARALLEL SAFE;
CREATE FUNCTION range()
RETURNS text[] AS $$
SELECT string_to_array(get_histogram_timings(), ',');
$$ LANGUAGE SQL;
CREATE FUNCTION pg_stat_monitor_internal(IN showtext boolean,
OUT bucket int8, -- 0
OUT userid oid,
OUT dbid oid,
OUT client_ip int8,
OUT queryid text, -- 4
OUT planid text,
OUT query text,
OUT query_plan text,
OUT state_code int8,
OUT top_queryid text,
OUT top_query text,
OUT application_name text,
OUT relations text, -- 11
OUT cmd_type int,
OUT elevel int,
OUT sqlcode TEXT,
OUT message text,
OUT bucket_start_time text,
OUT calls int8, -- 16
OUT total_time float8,
OUT min_time float8,
OUT max_time float8,
OUT mean_time float8,
OUT stddev_time float8,
OUT rows_retrieved int8,
OUT plans_calls int8, -- 23
OUT plan_total_time float8,
OUT plan_min_time float8,
OUT plan_max_time float8,
OUT plan_mean_time float8,
OUT plan_stddev_time float8,
OUT shared_blks_hit int8, -- 29
OUT shared_blks_read int8,
OUT shared_blks_dirtied int8,
OUT shared_blks_written int8,
OUT local_blks_hit int8,
OUT local_blks_read int8,
OUT local_blks_dirtied int8,
OUT local_blks_written int8,
OUT temp_blks_read int8,
OUT temp_blks_written int8,
OUT blk_read_time float8,
OUT blk_write_time float8,
OUT resp_calls text, -- 41
OUT cpu_user_time float8,
OUT cpu_sys_time float8,
OUT wal_records int8,
OUT wal_fpi int8,
OUT wal_bytes numeric,
OUT comments TEXT,
OUT toplevel BOOLEAN
)
RETURNS SETOF record
AS 'MODULE_PATHNAME', 'pg_stat_monitor'
LANGUAGE C STRICT VOLATILE PARALLEL SAFE;
CREATE OR REPLACE FUNCTION get_state(state_code int8) RETURNS TEXT AS
$$
SELECT
CASE
WHEN state_code = 0 THEN 'PARSING'
WHEN state_code = 1 THEN 'PLANNING'
WHEN state_code = 2 THEN 'ACTIVE'
WHEN state_code = 3 THEN 'FINISHED'
WHEN state_code = 4 THEN 'FINISHED WITH ERROR'
END
$$
LANGUAGE SQL PARALLEL SAFE;
CREATE or REPLACE FUNCTION get_cmd_type (cmd_type INTEGER) RETURNS TEXT AS
$$
SELECT
CASE
WHEN cmd_type = 0 THEN ''
WHEN cmd_type = 1 THEN 'SELECT'
WHEN cmd_type = 2 THEN 'UPDATE'
WHEN cmd_type = 3 THEN 'INSERT'
WHEN cmd_type = 4 THEN 'DELETE'
WHEN cmd_type = 5 THEN 'UTILITY'
WHEN cmd_type = 6 THEN 'NOTHING'
END
$$
LANGUAGE SQL PARALLEL SAFE;
CREATE FUNCTION pg_stat_monitor_settings(
OUT name text,
OUT value text,
OUT default_value text,
OUT description text,
OUT minimum INTEGER,
OUT maximum INTEGER,
OUT options text,
OUT restart text
)
RETURNS SETOF record
AS 'MODULE_PATHNAME', 'pg_stat_monitor_settings'
LANGUAGE C STRICT VOLATILE PARALLEL SAFE;
CREATE VIEW pg_stat_monitor_settings AS SELECT
name,
value,
default_value,
description,
minimum,
maximum,
options,
restart
FROM pg_stat_monitor_settings();
-- Register a view on the function for ease of use.
CREATE VIEW pg_stat_monitor AS SELECT
bucket,
bucket_start_time AS bucket_start_time,
userid::regrole,
datname,
'0.0.0.0'::inet + client_ip AS client_ip,
queryid,
top_queryid,
query,
comments,
planid,
query_plan,
top_query,
application_name,
string_to_array(relations, ',') AS relations,
cmd_type,
get_cmd_type(cmd_type) AS cmd_type_text,
elevel,
sqlcode,
message,
calls,
total_time,
min_time,
max_time,
mean_time,
stddev_time,
rows_retrieved,
shared_blks_hit,
shared_blks_read,
shared_blks_dirtied,
shared_blks_written,
local_blks_hit,
local_blks_read,
local_blks_dirtied,
local_blks_written,
temp_blks_read,
temp_blks_written,
blk_read_time,
blk_write_time,
(string_to_array(resp_calls, ',')) resp_calls,
cpu_user_time,
cpu_sys_time,
wal_records,
wal_fpi,
wal_bytes,
state_code,
get_state(state_code) as state
FROM pg_stat_monitor_internal(TRUE) p, pg_database d WHERE dbid = oid
ORDER BY bucket_start_time;
CREATE FUNCTION decode_error_level(elevel int)
RETURNS text
AS
$$
SELECT
CASE
WHEN elevel = 0 THEN ''
WHEN elevel = 10 THEN 'DEBUG5'
WHEN elevel = 11 THEN 'DEBUG4'
WHEN elevel = 12 THEN 'DEBUG3'
WHEN elevel = 13 THEN 'DEBUG2'
WHEN elevel = 14 THEN 'DEBUG1'
WHEN elevel = 15 THEN 'LOG'
WHEN elevel = 16 THEN 'LOG_SERVER_ONLY'
WHEN elevel = 17 THEN 'INFO'
WHEN elevel = 18 THEN 'NOTICE'
WHEN elevel = 19 THEN 'WARNING'
WHEN elevel = 20 THEN 'ERROR'
END
$$
LANGUAGE SQL PARALLEL SAFE;
CREATE OR REPLACE FUNCTION histogram(_bucket int, _quryid text)
RETURNS SETOF RECORD AS $$
DECLARE
rec record;
BEGIN
for rec in
with stat as (select queryid, bucket, unnest(range()) as range, unnest(resp_calls)::int freq from pg_stat_monitor) select range, freq, repeat('', (freq::float / max(freq) over() * 30)::int) as bar from stat where queryid = _quryid and bucket = _bucket
loop
return next rec;
end loop;
END
$$ language plpgsql;
-- CREATE FUNCTION pg_stat_monitor_hook_stats(
-- OUT hook text,
-- OUT min_time float8,
-- OUT max_time float8,
-- OUT total_time float8,
-- OUT ncalls int8
--)
--RETURNS SETOF record
--AS 'MODULE_PATHNAME', 'pg_stat_monitor_hook_stats'
--LANGUAGE C STRICT VOLATILE PARALLEL SAFE;
--CREATE VIEW pg_stat_monitor_hook_stats AS SELECT
-- hook,
-- min_time,
-- max_time,
-- total_time,
-- total_time / greatest(ncalls, 1) as avg_time,
-- ncalls,
-- ROUND(CAST(total_time / greatest(sum(total_time) OVER(), 0.00000001) * 100 as numeric), 2)::text || '%' as load_comparison
-- FROM pg_stat_monitor_hook_stats();
CREATE FUNCTION pg_stat_monitor_errors(
OUT severity int,
OUT message text,
OUT msgtime text,
OUT calls int8
)
RETURNS SETOF record
AS 'MODULE_PATHNAME', 'pg_stat_monitor_errors'
LANGUAGE C STRICT VOLATILE PARALLEL SAFE;
CREATE OR REPLACE FUNCTION pgsm_log_severity_as_text(severity int) RETURNS TEXT AS
$$
SELECT
CASE
WHEN severity = 0 THEN 'INFO'
WHEN severity = 1 THEN 'WARNING'
WHEN severity = 2 THEN 'ERROR'
END
$$
LANGUAGE SQL PARALLEL SAFE;
CREATE VIEW pg_stat_monitor_errors AS SELECT
pgsm_log_severity_as_text(severity) as severity, message, msgtime, calls
FROM pg_stat_monitor_errors();
CREATE FUNCTION pg_stat_monitor_reset_errors()
RETURNS void
AS 'MODULE_PATHNAME'
LANGUAGE C PARALLEL SAFE;
GRANT SELECT ON pg_stat_monitor TO PUBLIC;
GRANT SELECT ON pg_stat_monitor_settings TO PUBLIC;
GRANT SELECT ON pg_stat_monitor_errors TO PUBLIC;
-- Don't want this to be available to non-superusers.
REVOKE ALL ON FUNCTION pg_stat_monitor_reset() FROM PUBLIC;
REVOKE ALL ON FUNCTION pg_stat_monitor_reset_errors() FROM PUBLIC;

File diff suppressed because it is too large Load Diff

View File

@ -52,6 +52,7 @@
#include "utils/timestamp.h"
#include "utils/lsyscache.h"
#include "utils/guc.h"
#include "utils/guc_tables.h"
#define MAX_BACKEND_PROCESES (MaxBackends + NUM_AUXILIARY_PROCS + max_prepared_xacts)
#define IntArrayGetTextDatum(x,y) intarray_get_datum(x,y)
@ -87,21 +88,23 @@
#define MAX_QUERY_BUF (PGSM_QUERY_SHARED_BUFFER * 1024 * 1024)
#define MAX_BUCKETS_MEM (PGSM_MAX * 1024 * 1024)
#define BUCKETS_MEM_OVERFLOW() ((hash_get_num_entries(pgss_hash) * sizeof(pgssEntry)) >= MAX_BUCKETS_MEM)
#define MAX_QUERY_BUFFER_BUCKET MAX_QUERY_BUF / PGSM_MAX_BUCKETS
#define MAX_BUCKET_ENTRIES (MAX_BUCKETS_MEM / sizeof(pgssEntry))
#define QUERY_BUFFER_OVERFLOW(x,y) ((x + y + sizeof(uint64) + sizeof(uint64)) > MAX_QUERY_BUFFER_BUCKET)
#define QUERY_BUFFER_OVERFLOW(x,y) ((x + y + sizeof(uint64) + sizeof(uint64)) > MAX_QUERY_BUF)
#define QUERY_MARGIN 100
#define MIN_QUERY_LEN 10
#define SQLCODE_LEN 20
#if PG_VERSION_NUM >= 130000
#define MAX_SETTINGS 14
#define MAX_SETTINGS 15
#else
#define MAX_SETTINGS 13
#define MAX_SETTINGS 14
#endif
/* Update this if need a enum GUC with more options. */
#define MAX_ENUM_OPTIONS 6
typedef struct GucVariables
{
enum config_type type; /* PGC_BOOL, PGC_INT, PGC_REAL, PGC_STRING, PGC_ENUM */
int guc_variable;
char guc_name[TEXT_LEN];
char guc_desc[TEXT_LEN];
@ -111,6 +114,8 @@ typedef struct GucVariables
int guc_unit;
int *guc_value;
bool guc_restart;
int n_options;
char guc_options[MAX_ENUM_OPTIONS][32];
} GucVariable;
#if PG_VERSION_NUM < 130000
@ -161,7 +166,7 @@ typedef enum AGG_KEY
#define MAX_QUERY_LEN 1024
/* shared nenory storage for the query */
/* shared memory storage for the query */
typedef struct CallTime
{
double total_time; /* total execution time, in msec */
@ -171,27 +176,26 @@ typedef struct CallTime
double sum_var_time; /* sum of variances in execution time in msec */
} CallTime;
typedef struct pgssQueryHashKey
{
uint64 bucket_id; /* bucket number */
uint64 queryid; /* query identifier */
uint64 userid; /* user OID */
uint64 dbid; /* database OID */
uint64 ip; /* client ip address */
uint64 appid; /* hash of application name */
} pgssQueryHashKey;
/*
* Entry type for queries hash table (query ID).
*
* We use a hash table to keep track of query IDs that have their
* corresponding query text added to the query buffer (pgsm_query_shared_buffer).
*
* This allow us to avoid adding duplicated queries to the buffer, therefore
* leaving more space for other queries and saving some CPU.
*/
typedef struct pgssQueryEntry
{
pgssQueryHashKey key; /* hash key of entry - MUST BE FIRST */
uint64 pos; /* bucket number */
uint64 state;
uint64 queryid; /* query identifier, also the key. */
size_t query_pos; /* query location within query buffer */
} pgssQueryEntry;
typedef struct PlanInfo
{
uint64 planid; /* plan identifier */
char plan_text[PLAN_TEXT_LEN]; /* plan text */
size_t plan_len; /* strlen(plan_text) */
} PlanInfo;
typedef struct pgssHashKey
@ -203,14 +207,11 @@ typedef struct pgssHashKey
uint64 ip; /* client ip address */
uint64 planid; /* plan identifier */
uint64 appid; /* hash of application name */
uint64 toplevel; /* query executed at top level */
} pgssHashKey;
typedef struct QueryInfo
{
uint64 queryid; /* query identifier */
Oid userid; /* user OID */
Oid dbid; /* database OID */
uint host; /* client IP */
uint64 parentid; /* parent queryid of current query*/
int64 type; /* type of query, options are query, info, warning, error, fatal */
char application_name[APPLICATIONNAME_LEN];
@ -310,8 +311,22 @@ typedef struct pgssSharedState
pg_atomic_uint64 current_wbucket;
pg_atomic_uint64 prev_bucket_usec;
uint64 bucket_entry[MAX_BUCKETS];
int64 query_buf_size_bucket;
char bucket_start_time[MAX_BUCKETS][60]; /* start time of the bucket */
LWLock *errors_lock; /* protects errors hashtable search/modification */
/*
* These variables are used when pgsm_overflow_target is ON.
*
* overflow is set to true when the query buffer overflows.
*
* n_bucket_cycles counts the number of times we changed bucket
* since the query buffer overflowed. When it reaches pgsm_max_buckets
* we remove the dump file, also reset the counter.
*
* This allows us to avoid having a large file on disk that would also
* slowdown queries to the pg_stat_monitor view.
*/
bool overflow;
size_t n_bucket_cycles;
} pgssSharedState;
#define ResetSharedState(x) \
@ -373,6 +388,7 @@ bool SaveQueryText(uint64 bucketid,
void init_guc(void);
GucVariable *get_conf(int i);
bool IsSystemInitialized(void);
/* hash_create.c */
bool IsHashInitialize(void);
void pgss_shmem_startup(void);
@ -381,39 +397,55 @@ int pgsm_get_bucket_size(void);
pgssSharedState* pgsm_get_ss(void);
HTAB *pgsm_get_plan_hash(void);
HTAB *pgsm_get_hash(void);
HTAB *pgsm_get_query_hash(void);
HTAB *pgsm_get_plan_hash(void);
void hash_entry_reset(void);
void hash_query_entryies_reset(void);
void hash_query_entries();
void hash_query_entry_dealloc(int new_bucket_id, int old_bucket_id, unsigned char *query_buffer[]);
void hash_entry_dealloc(int new_bucket_id, int old_bucket_id, unsigned char *query_buffer[]);
void hash_entry_dealloc(int new_bucket_id, int old_bucket_id, unsigned char *query_buffer);
pgssEntry* hash_entry_alloc(pgssSharedState *pgss, pgssHashKey *key, int encoding);
Size hash_memsize(void);
int read_query_buffer(int bucket_id, uint64 queryid, char *query_txt);
int read_query_buffer(int bucket_id, uint64 queryid, char *query_txt, size_t pos);
uint64 read_query(unsigned char *buf, uint64 queryid, char * query, size_t pos);
pgssQueryEntry* hash_find_query_entry(uint64 bucket_id, uint64 queryid, uint64 dbid, uint64 userid, uint64 ip, uint64 appid);
pgssQueryEntry* hash_create_query_entry(uint64 bucket_id, uint64 queryid, uint64 dbid, uint64 userid, uint64 ip, uint64 appid);
void pgss_startup(void);
void set_qbuf(int i, unsigned char *);
void set_qbuf(unsigned char *);
/* hash_query.c */
void pgss_startup(void);
/*---- GUC variables ----*/
typedef enum {
PSGM_TRACK_NONE, /* track no statements */
PGSM_TRACK_TOP, /* only top level statements */
PGSM_TRACK_ALL /* all statements, including nested ones */
} PGSMTrackLevel;
static const struct config_enum_entry track_options[] =
{
{"none", PSGM_TRACK_NONE, false},
{"top", PGSM_TRACK_TOP, false},
{"all", PGSM_TRACK_ALL, false},
{NULL, 0, false}
};
#define PGSM_MAX get_conf(0)->guc_variable
#define PGSM_QUERY_MAX_LEN get_conf(1)->guc_variable
#define PGSM_ENABLED get_conf(2)->guc_variable
#define PGSM_TRACK_UTILITY get_conf(3)->guc_variable
#define PGSM_NORMALIZED_QUERY get_conf(4)->guc_variable
#define PGSM_MAX_BUCKETS get_conf(5)->guc_variable
#define PGSM_BUCKET_TIME get_conf(6)->guc_variable
#define PGSM_HISTOGRAM_MIN get_conf(7)->guc_variable
#define PGSM_HISTOGRAM_MAX get_conf(8)->guc_variable
#define PGSM_HISTOGRAM_BUCKETS get_conf(9)->guc_variable
#define PGSM_QUERY_SHARED_BUFFER get_conf(10)->guc_variable
#define PGSM_OVERFLOW_TARGET get_conf(12)->guc_variable
#define PGSM_QUERY_PLAN get_conf(12)->guc_variable
#define PGSM_TRACK_PLANNING get_conf(13)->guc_variable
#define PGSM_TRACK_UTILITY get_conf(2)->guc_variable
#define PGSM_NORMALIZED_QUERY get_conf(3)->guc_variable
#define PGSM_MAX_BUCKETS get_conf(4)->guc_variable
#define PGSM_BUCKET_TIME get_conf(5)->guc_variable
#define PGSM_HISTOGRAM_MIN get_conf(6)->guc_variable
#define PGSM_HISTOGRAM_MAX get_conf(7)->guc_variable
#define PGSM_HISTOGRAM_BUCKETS get_conf(8)->guc_variable
#define PGSM_QUERY_SHARED_BUFFER get_conf(9)->guc_variable
#define PGSM_OVERFLOW_TARGET get_conf(10)->guc_variable
#define PGSM_QUERY_PLAN get_conf(11)->guc_variable
#define PGSM_TRACK get_conf(12)->guc_variable
#define PGSM_EXTRACT_COMMENTS get_conf(13)->guc_variable
#define PGSM_TRACK_PLANNING get_conf(14)->guc_variable
/*---- Benchmarking ----*/
#ifdef BENCHMARK

223
pgsm_errors.c Normal file
View File

@ -0,0 +1,223 @@
/*-------------------------------------------------------------------------
*
* pgsm_errors.c
* Track pg_stat_monitor internal error messages.
*
* Copyright © 2021, Percona LLC and/or its affiliates
*
* Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
*
* Portions Copyright (c) 1994, The Regents of the University of California
*
* IDENTIFICATION
* contrib/pg_stat_monitor/pgsm_errors.c
*
*-------------------------------------------------------------------------
*/
#include <stdarg.h>
#include <stdbool.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>
#include <postgres.h>
#include <access/hash.h>
#include <storage/shmem.h>
#include <utils/hsearch.h>
#include "pg_stat_monitor.h"
#include "pgsm_errors.h"
PG_FUNCTION_INFO_V1(pg_stat_monitor_errors);
PG_FUNCTION_INFO_V1(pg_stat_monitor_reset_errors);
/*
* Maximum number of error messages tracked.
* This should be set to a sensible value in order to track
* the different type of errors that pg_stat_monitor may
* report, e.g. out of memory.
*/
#define PSGM_ERRORS_MAX 128
static HTAB *pgsm_errors_ht = NULL;
void psgm_errors_init(void)
{
HASHCTL info;
#if PG_VERSION_NUM >= 140000
int flags = HASH_ELEM | HASH_STRINGS;
#else
int flags = HASH_ELEM | HASH_BLOBS;
#endif
memset(&info, 0, sizeof(info));
info.keysize = ERROR_MSG_MAX_LEN;
info.entrysize = sizeof(ErrorEntry);
pgsm_errors_ht = ShmemInitHash("pg_stat_monitor: errors hashtable",
PSGM_ERRORS_MAX, /* initial size */
PSGM_ERRORS_MAX, /* maximum size */
&info,
flags);
}
size_t pgsm_errors_size(void)
{
return hash_estimate_size(PSGM_ERRORS_MAX, sizeof(ErrorEntry));
}
void pgsm_log(PgsmLogSeverity severity, const char *format, ...)
{
char key[ERROR_MSG_MAX_LEN];
ErrorEntry *entry;
bool found = false;
va_list ap;
int n;
struct timeval tv;
struct tm *lt;
pgssSharedState *pgss;
va_start(ap, format);
n = vsnprintf(key, ERROR_MSG_MAX_LEN, format, ap);
va_end(ap);
if (n < 0)
return;
pgss = pgsm_get_ss();
LWLockAcquire(pgss->errors_lock, LW_EXCLUSIVE);
entry = (ErrorEntry *) hash_search(pgsm_errors_ht, key, HASH_ENTER_NULL, &found);
if (!entry)
{
LWLockRelease(pgss->errors_lock);
/*
* We're out of memory, can't track this error message.
*/
return;
}
if (!found)
{
entry->severity = severity;
entry->calls = 0;
}
/* Update message timestamp. */
gettimeofday(&tv, NULL);
lt = localtime(&tv.tv_sec);
snprintf(entry->time, sizeof(entry->time),
"%04d-%02d-%02d %02d:%02d:%02d",
lt->tm_year + 1900,
lt->tm_mon + 1,
lt->tm_mday,
lt->tm_hour,
lt->tm_min,
lt->tm_sec);
entry->calls++;
LWLockRelease(pgss->errors_lock);
}
/*
* Clear all entries from the hash table.
*/
Datum
pg_stat_monitor_reset_errors(PG_FUNCTION_ARGS)
{
HASH_SEQ_STATUS hash_seq;
ErrorEntry *entry;
pgssSharedState *pgss = pgsm_get_ss();
/* Safety check... */
if (!IsSystemInitialized())
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("pg_stat_monitor: must be loaded via shared_preload_libraries")));
LWLockAcquire(pgss->errors_lock, LW_EXCLUSIVE);
hash_seq_init(&hash_seq, pgsm_errors_ht);
while ((entry = hash_seq_search(&hash_seq)) != NULL)
entry = hash_search(pgsm_errors_ht, &entry->message, HASH_REMOVE, NULL);
LWLockRelease(pgss->errors_lock);
PG_RETURN_VOID();
}
/*
* Invoked when users query the view pg_stat_monitor_errors.
* This function creates tuples with error messages from data present in
* the hash table, then return the dataset to the caller.
*/
Datum
pg_stat_monitor_errors(PG_FUNCTION_ARGS)
{
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
TupleDesc tupdesc;
Tuplestorestate *tupstore;
MemoryContext per_query_ctx;
MemoryContext oldcontext;
HASH_SEQ_STATUS hash_seq;
ErrorEntry *error_entry;
pgssSharedState *pgss = pgsm_get_ss();
/* Safety check... */
if (!IsSystemInitialized())
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("pg_stat_monitor: must be loaded via shared_preload_libraries")));
/* check to see if caller supports us returning a tuplestore */
if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("pg_stat_monitor: set-valued function called in context that cannot accept a set")));
/* Switch into long-lived context to construct returned data structures */
per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
oldcontext = MemoryContextSwitchTo(per_query_ctx);
/* Build a tuple descriptor for our result type */
if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
elog(ERROR, "pg_stat_monitor: return type must be a row type");
if (tupdesc->natts != 4)
elog(ERROR, "pg_stat_monitor: incorrect number of output arguments, required 3, found %d", tupdesc->natts);
tupstore = tuplestore_begin_heap(true, false, work_mem);
rsinfo->returnMode = SFRM_Materialize;
rsinfo->setResult = tupstore;
rsinfo->setDesc = tupdesc;
MemoryContextSwitchTo(oldcontext);
LWLockAcquire(pgss->errors_lock, LW_SHARED);
hash_seq_init(&hash_seq, pgsm_errors_ht);
while ((error_entry = hash_seq_search(&hash_seq)) != NULL)
{
Datum values[4];
bool nulls[4];
int i = 0;
memset(values, 0, sizeof(values));
memset(nulls, 0, sizeof(nulls));
values[i++] = Int64GetDatumFast(error_entry->severity);
values[i++] = CStringGetTextDatum(error_entry->message);
values[i++] = CStringGetTextDatum(error_entry->time);
values[i++] = Int64GetDatumFast(error_entry->calls);
tuplestore_putvalues(tupstore, tupdesc, values, nulls);
}
LWLockRelease(pgss->errors_lock);
/* clean up and return the tuplestore */
tuplestore_donestoring(tupstore);
return (Datum)0;
}

65
pgsm_errors.h Normal file
View File

@ -0,0 +1,65 @@
/*-------------------------------------------------------------------------
*
* pgsm_errors.h
* Track pg_stat_monitor internal error messages.
*
* Copyright © 2021, Percona LLC and/or its affiliates
*
* Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
*
* Portions Copyright (c) 1994, The Regents of the University of California
*
* IDENTIFICATION
* contrib/pg_stat_monitor/pgsm_errors.h
*
*-------------------------------------------------------------------------
*/
#ifndef PGSM_ERRORS_H
#define PGSM_ERRORS_H
#include <stddef.h>
#include <postgres.h>
/* Maximum allowed error message length. */
#define ERROR_MSG_MAX_LEN 128
/* Log message severity. */
typedef enum {
PSGM_LOG_INFO,
PGSM_LOG_WARNING,
PGSM_LOG_ERROR
} PgsmLogSeverity;
typedef struct {
char message[ERROR_MSG_MAX_LEN]; /* message is also the hash key (MUST BE FIRST). */
PgsmLogSeverity severity;
char time[60]; /* last timestamp in which this error was reported. */
int64 calls; /* how many times this error was reported. */
} ErrorEntry;
/*
* Must be called during module startup, creates the hash table
* used to store pg_stat_monitor error messages.
*/
void psgm_errors_init(void);
/*
* Returns an estimate of the hash table size.
* Used to reserve space on Postmaster's shared memory.
*/
size_t pgsm_errors_size(void);
/*
* Add a message to the hash table.
* Increment no. of calls if message already exists.
*/
void pgsm_log(PgsmLogSeverity severity, const char *format, ...);
#define pgsm_log_info(msg, ...) pgsm_log(PGSM_LOG_INFO, msg, ##__VA_ARGS__)
#define pgsm_log_warning(msg, ...) pgsm_log(PGSM_LOG_WARNING, msg, ##__VA_ARGS__)
#define pgsm_log_error(msg, ...) pgsm_log(PGSM_LOG_ERROR, msg, ##__VA_ARGS__)
#endif /* PGSM_ERRORS_H */

View File

@ -13,10 +13,10 @@ SELECT 1 AS num;
SELECT query,application_name FROM pg_stat_monitor ORDER BY query COLLATE "C";
query | application_name
--------------------------------------------------------------------------------+-----------------------------
-------------------------------------------------------------------------------+-----------------------------
SELECT $1 AS num | pg_regress/application_name
SELECT pg_stat_monitor_reset(); | pg_regress/application_name
SELECT query,application_name FROM pg_stat_monitor ORDER BY query COLLATE "C"; | pg_regress/application_name
SELECT pg_stat_monitor_reset() | pg_regress/application_name
SELECT query,application_name FROM pg_stat_monitor ORDER BY query COLLATE "C" | pg_regress/application_name
(3 rows)
SELECT pg_stat_monitor_reset();

View File

@ -13,10 +13,10 @@ SELECT 1 AS num;
SELECT query FROM pg_stat_monitor ORDER BY query COLLATE "C";
query
---------------------------------------------------------------
--------------------------------------------------------------
SELECT $1 AS num
SELECT pg_stat_monitor_reset();
SELECT query FROM pg_stat_monitor ORDER BY query COLLATE "C";
SELECT pg_stat_monitor_reset()
SELECT query FROM pg_stat_monitor ORDER BY query COLLATE "C"
(3 rows)
SELECT pg_stat_monitor_reset();

View File

@ -23,21 +23,23 @@ SELECT b FROM t2 FOR UPDATE;
TRUNCATE t1;
DROP TABLE t1;
DROP TABLE t2;
SELECT query, cmd_type, cmd_type_text FROM pg_stat_monitor ORDER BY query COLLATE "C";
query | cmd_type | cmd_type_text
-----------------------------------------------------------------------------------------+----------+---------------
CREATE TABLE t1 (a INTEGER); | 0 |
CREATE TABLE t2 (b INTEGER); | 0 |
DELETE FROM t1; | 4 | DELETE
DROP TABLE t1; | 0 |
----------------------------------------------------------------------------------------+----------+---------------
CREATE TABLE t1 (a INTEGER) | 0 |
CREATE TABLE t2 (b INTEGER) | 0 |
DELETE FROM t1 | 4 | DELETE
DROP TABLE t1 | 0 |
DROP TABLE t2 | 0 |
INSERT INTO t1 VALUES($1) | 3 | INSERT
SELECT a FROM t1; | 1 | SELECT
SELECT b FROM t2 FOR UPDATE; | 1 | SELECT
SELECT pg_stat_monitor_reset(); | 1 | SELECT
SELECT query, cmd_type, cmd_type_text FROM pg_stat_monitor ORDER BY query COLLATE "C"; | 1 | SELECT
TRUNCATE t1; | 0 |
SELECT a FROM t1 | 1 | SELECT
SELECT b FROM t2 FOR UPDATE | 1 | SELECT
SELECT pg_stat_monitor_reset() | 1 | SELECT
SELECT query, cmd_type, cmd_type_text FROM pg_stat_monitor ORDER BY query COLLATE "C" | 1 | SELECT
TRUNCATE t1 | 0 |
UPDATE t1 SET a = $1 | 2 | UPDATE
(11 rows)
(12 rows)
SELECT pg_stat_monitor_reset();
pg_stat_monitor_reset

View File

@ -37,10 +37,10 @@ SELECT a,b,c,d FROM t1, t2, t3, t4 WHERE t1.a = t2.b AND t3.c = t4.d ORDER BY a;
SELECT query,calls FROM pg_stat_monitor ORDER BY query COLLATE "C";
query | calls
----------------------------------------------------------------------------------+-------
SELECT a,b,c,d FROM t1, t2, t3, t4 WHERE t1.a = t2.b AND t3.c = t4.d ORDER BY a; | 4
SELECT pg_stat_monitor_reset(); | 1
SELECT query,calls FROM pg_stat_monitor ORDER BY query COLLATE "C"; | 1
---------------------------------------------------------------------------------+-------
SELECT a,b,c,d FROM t1, t2, t3, t4 WHERE t1.a = t2.b AND t3.c = t4.d ORDER BY a | 4
SELECT pg_stat_monitor_reset() | 1
SELECT query,calls FROM pg_stat_monitor ORDER BY query COLLATE "C" | 1
(3 rows)
SELECT pg_stat_monitor_reset();
@ -69,8 +69,8 @@ SELECT query,calls FROM pg_stat_monitor ORDER BY query COLLATE "C";
query | calls
---------------------------------------------------------------------------------------------------+-------
SELECT a,b,c,d FROM t1, t2, t3, t4 WHERE t1.a = t2.b AND t3.c = t4.d ORDER BY a | 1000
SELECT pg_stat_monitor_reset(); | 1
SELECT query,calls FROM pg_stat_monitor ORDER BY query COLLATE "C"; | 1
SELECT pg_stat_monitor_reset() | 1
SELECT query,calls FROM pg_stat_monitor ORDER BY query COLLATE "C" | 1
do $$ +| 1
declare +|
n integer:= 1; +|
@ -80,7 +80,7 @@ SELECT query,calls FROM pg_stat_monitor ORDER BY query COLLATE "C";
exit when n = 1000; +|
n := n + 1; +|
end loop; +|
end $$; |
end $$ |
(4 rows)
SELECT pg_stat_monitor_reset();

View File

@ -29,11 +29,11 @@ SELECT * FROM t3,t4 WHERE t3.c = t4.d;
\c contrib_regression
SELECT datname, query FROM pg_stat_monitor ORDER BY query COLLATE "C";
datname | query
--------------------+------------------------------------------------------------------------
db1 | SELECT * FROM t1,t2 WHERE t1.a = t2.b;
db2 | SELECT * FROM t3,t4 WHERE t3.c = t4.d;
contrib_regression | SELECT datname, query FROM pg_stat_monitor ORDER BY query COLLATE "C";
contrib_regression | SELECT pg_stat_monitor_reset();
--------------------+-----------------------------------------------------------------------
db1 | SELECT * FROM t1,t2 WHERE t1.a = t2.b
db2 | SELECT * FROM t3,t4 WHERE t3.c = t4.d
contrib_regression | SELECT datname, query FROM pg_stat_monitor ORDER BY query COLLATE "C"
contrib_regression | SELECT pg_stat_monitor_reset()
(4 rows)
SELECT pg_stat_monitor_reset();

View File

@ -22,17 +22,21 @@ END $$;
WARNING: warning message
SELECT query, elevel, sqlcode, message FROM pg_stat_monitor ORDER BY query COLLATE "C",elevel;
query | elevel | sqlcode | message
------------------------------------------------------------------------------------------------+--------+---------+-----------------------------------
-----------------------------------------------------------------------------------------------+--------+---------+-----------------------------------
ELECET * FROM unknown; | 20 | 42601 | syntax error at or near "ELECET"
SELECT * FROM unknown; | 20 | 42P01 | relation "unknown" does not exist
SELECT 1/0; | 20 | 22012 | division by zero
SELECT pg_stat_monitor_reset(); | 0 | |
SELECT query, elevel, sqlcode, message FROM pg_stat_monitor ORDER BY query COLLATE "C",elevel; | 0 | |
SELECT pg_stat_monitor_reset() | 0 | |
SELECT query, elevel, sqlcode, message FROM pg_stat_monitor ORDER BY query COLLATE "C",elevel | 0 | |
do $$ +| 0 | |
BEGIN +| | |
RAISE WARNING 'warning message'; +| | |
END $$ | | |
do $$ +| 19 | 01000 | warning message
BEGIN +| | |
RAISE WARNING 'warning message'; +| | |
END $$; | | |
(6 rows)
(7 rows)
SELECT pg_stat_monitor_reset();
pg_stat_monitor_reset

View File

@ -22,17 +22,21 @@ END $$;
WARNING: warning message
SELECT query, elevel, sqlcode, message FROM pg_stat_monitor ORDER BY query COLLATE "C",elevel;
query | elevel | sqlcode | message
------------------------------------------------------------------------------------------------+--------+---------+-----------------------------------
-----------------------------------------------------------------------------------------------+--------+---------+-----------------------------------
ELECET * FROM unknown; | 21 | 42601 | syntax error at or near "ELECET"
SELECT * FROM unknown; | 21 | 42P01 | relation "unknown" does not exist
SELECT 1/0; | 21 | 22012 | division by zero
SELECT pg_stat_monitor_reset(); | 0 | |
SELECT query, elevel, sqlcode, message FROM pg_stat_monitor ORDER BY query COLLATE "C",elevel; | 0 | |
SELECT pg_stat_monitor_reset() | 0 | |
SELECT query, elevel, sqlcode, message FROM pg_stat_monitor ORDER BY query COLLATE "C",elevel | 0 | |
do $$ +| 0 | |
BEGIN +| | |
RAISE WARNING 'warning message'; +| | |
END $$ | | |
do $$ +| 19 | 01000 | warning message
BEGIN +| | |
RAISE WARNING 'warning message'; +| | |
END $$; | | |
(6 rows)
(7 rows)
SELECT pg_stat_monitor_reset();
pg_stat_monitor_reset

View File

@ -12,23 +12,24 @@ select pg_sleep(.5);
(1 row)
SELECT * FROM pg_stat_monitor_settings ORDER BY name COLLATE "C";
name | value | default_value | description | minimum | maximum | restart
------------------------------------------+--------+---------------+----------------------------------------------------------------------------------------------------------+---------+------------+---------
pg_stat_monitor.pgsm_bucket_time | 60 | 60 | Sets the time in seconds per bucket. | 1 | 2147483647 | 1
pg_stat_monitor.pgsm_enable | 1 | 1 | Enable/Disable statistics collector. | 0 | 0 | 0
pg_stat_monitor.pgsm_enable_query_plan | 0 | 0 | Enable/Disable query plan monitoring | 0 | 0 | 0
pg_stat_monitor.pgsm_histogram_buckets | 10 | 10 | Sets the maximum number of histogram buckets | 2 | 2147483647 | 1
pg_stat_monitor.pgsm_histogram_max | 100000 | 100000 | Sets the time in millisecond. | 10 | 2147483647 | 1
pg_stat_monitor.pgsm_histogram_min | 0 | 0 | Sets the time in millisecond. | 0 | 2147483647 | 1
pg_stat_monitor.pgsm_max | 100 | 100 | Sets the maximum size of shared memory in (MB) used for statement's metadata tracked by pg_stat_monitor. | 1 | 1000 | 1
pg_stat_monitor.pgsm_max_buckets | 10 | 10 | Sets the maximum number of buckets. | 1 | 10 | 1
pg_stat_monitor.pgsm_normalized_query | 1 | 1 | Selects whether save query in normalized format. | 0 | 0 | 0
pg_stat_monitor.pgsm_overflow_target | 0 | 1 | Sets the overflow target for pg_stat_monitor | 0 | 1 | 1
pg_stat_monitor.pgsm_query_max_len | 1024 | 1024 | Sets the maximum length of query. | 1024 | 2147483647 | 1
pg_stat_monitor.pgsm_query_shared_buffer | 20 | 20 | Sets the maximum size of shared memory in (MB) used for query tracked by pg_stat_monitor. | 1 | 10000 | 1
pg_stat_monitor.pgsm_track_planning | 1 | 1 | Selects whether planning statistics are tracked. | 0 | 0 | 0
pg_stat_monitor.pgsm_track_utility | 1 | 1 | Selects whether utility commands are tracked. | 0 | 0 | 0
(14 rows)
name | value | default_value | description | minimum | maximum | options | restart
------------------------------------------+--------+---------------+----------------------------------------------------------------------------------------------------------+---------+------------+----------------+---------
pg_stat_monitor.extract_comments | no | no | Enable/Disable extracting comments from queries. | | | yes, no | no
pg_stat_monitor.pgsm_bucket_time | 60 | 60 | Sets the time in seconds per bucket. | 1 | 2147483647 | | yes
pg_stat_monitor.pgsm_enable_query_plan | no | no | Enable/Disable query plan monitoring | | | yes, no | no
pg_stat_monitor.pgsm_histogram_buckets | 10 | 10 | Sets the maximum number of histogram buckets | 2 | 2147483647 | | yes
pg_stat_monitor.pgsm_histogram_max | 100000 | 100000 | Sets the time in millisecond. | 10 | 2147483647 | | yes
pg_stat_monitor.pgsm_histogram_min | 0 | 0 | Sets the time in millisecond. | 0 | 2147483647 | | yes
pg_stat_monitor.pgsm_max | 100 | 100 | Sets the maximum size of shared memory in (MB) used for statement's metadata tracked by pg_stat_monitor. | 1 | 1000 | | yes
pg_stat_monitor.pgsm_max_buckets | 10 | 10 | Sets the maximum number of buckets. | 1 | 10 | | yes
pg_stat_monitor.pgsm_normalized_query | yes | yes | Selects whether save query in normalized format. | | | yes, no | no
pg_stat_monitor.pgsm_overflow_target | 1 | 1 | Sets the overflow target for pg_stat_monitor | 0 | 1 | | yes
pg_stat_monitor.pgsm_query_max_len | 1024 | 1024 | Sets the maximum length of query. | 1024 | 2147483647 | | yes
pg_stat_monitor.pgsm_query_shared_buffer | 20 | 20 | Sets the maximum size of shared memory in (MB) used for query tracked by pg_stat_monitor. | 1 | 10000 | | yes
pg_stat_monitor.pgsm_track_planning | no | no | Selects whether planning statistics are tracked. | | | yes, no | no
pg_stat_monitor.pgsm_track_utility | yes | yes | Selects whether utility commands are tracked. | | | yes, no | no
pg_stat_monitor.track | top | top | Selects which statements are tracked by pg_stat_monitor. | | | none, top, all | no
(15 rows)
SELECT pg_stat_monitor_reset();
pg_stat_monitor_reset

View File

@ -12,22 +12,23 @@ select pg_sleep(.5);
(1 row)
SELECT * FROM pg_stat_monitor_settings ORDER BY name COLLATE "C";
name | value | default_value | description | minimum | maximum | restart
------------------------------------------+--------+---------------+----------------------------------------------------------------------------------------------------------+---------+------------+---------
pg_stat_monitor.pgsm_bucket_time | 60 | 60 | Sets the time in seconds per bucket. | 1 | 2147483647 | 1
pg_stat_monitor.pgsm_enable | 1 | 1 | Enable/Disable statistics collector. | 0 | 0 | 0
pg_stat_monitor.pgsm_enable_query_plan | 0 | 0 | Enable/Disable query plan monitoring | 0 | 0 | 0
pg_stat_monitor.pgsm_histogram_buckets | 10 | 10 | Sets the maximum number of histogram buckets | 2 | 2147483647 | 1
pg_stat_monitor.pgsm_histogram_max | 100000 | 100000 | Sets the time in millisecond. | 10 | 2147483647 | 1
pg_stat_monitor.pgsm_histogram_min | 0 | 0 | Sets the time in millisecond. | 0 | 2147483647 | 1
pg_stat_monitor.pgsm_max | 100 | 100 | Sets the maximum size of shared memory in (MB) used for statement's metadata tracked by pg_stat_monitor. | 1 | 1000 | 1
pg_stat_monitor.pgsm_max_buckets | 10 | 10 | Sets the maximum number of buckets. | 1 | 10 | 1
pg_stat_monitor.pgsm_normalized_query | 1 | 1 | Selects whether save query in normalized format. | 0 | 0 | 0
pg_stat_monitor.pgsm_overflow_target | 0 | 1 | Sets the overflow target for pg_stat_monitor | 0 | 1 | 1
pg_stat_monitor.pgsm_query_max_len | 1024 | 1024 | Sets the maximum length of query. | 1024 | 2147483647 | 1
pg_stat_monitor.pgsm_query_shared_buffer | 20 | 20 | Sets the maximum size of shared memory in (MB) used for query tracked by pg_stat_monitor. | 1 | 10000 | 1
pg_stat_monitor.pgsm_track_utility | 1 | 1 | Selects whether utility commands are tracked. | 0 | 0 | 0
(13 rows)
name | value | default_value | description | minimum | maximum | options | restart
------------------------------------------+--------+---------------+----------------------------------------------------------------------------------------------------------+---------+------------+----------------+---------
pg_stat_monitor.extract_comments | no | no | Enable/Disable extracting comments from queries. | | | yes, no | no
pg_stat_monitor.pgsm_bucket_time | 60 | 60 | Sets the time in seconds per bucket. | 1 | 2147483647 | | yes
pg_stat_monitor.pgsm_enable_query_plan | no | no | Enable/Disable query plan monitoring | | | yes, no | no
pg_stat_monitor.pgsm_histogram_buckets | 10 | 10 | Sets the maximum number of histogram buckets | 2 | 2147483647 | | yes
pg_stat_monitor.pgsm_histogram_max | 100000 | 100000 | Sets the time in millisecond. | 10 | 2147483647 | | yes
pg_stat_monitor.pgsm_histogram_min | 0 | 0 | Sets the time in millisecond. | 0 | 2147483647 | | yes
pg_stat_monitor.pgsm_max | 100 | 100 | Sets the maximum size of shared memory in (MB) used for statement's metadata tracked by pg_stat_monitor. | 1 | 1000 | | yes
pg_stat_monitor.pgsm_max_buckets | 10 | 10 | Sets the maximum number of buckets. | 1 | 10 | | yes
pg_stat_monitor.pgsm_normalized_query | yes | yes | Selects whether save query in normalized format. | | | yes, no | no
pg_stat_monitor.pgsm_overflow_target | 1 | 1 | Sets the overflow target for pg_stat_monitor | 0 | 1 | | yes
pg_stat_monitor.pgsm_query_max_len | 1024 | 1024 | Sets the maximum length of query. | 1024 | 2147483647 | | yes
pg_stat_monitor.pgsm_query_shared_buffer | 20 | 20 | Sets the maximum size of shared memory in (MB) used for query tracked by pg_stat_monitor. | 1 | 10000 | | yes
pg_stat_monitor.pgsm_track_utility | yes | yes | Selects whether utility commands are tracked. | | | yes, no | no
pg_stat_monitor.track | top | top | Selects which statements are tracked by pg_stat_monitor. | | | none, top, all | no
(14 rows)
SELECT pg_stat_monitor_reset();
pg_stat_monitor_reset

View File

@ -38,13 +38,13 @@ SELECT * FROM foo1, foo2, foo3, foo4;
SELECT query, relations from pg_stat_monitor ORDER BY query collate "C";
query | relations
--------------------------------------------------------------------------+---------------------------------------------------
SELECT * FROM foo1, foo2, foo3, foo4; | {public.foo1,public.foo2,public.foo3,public.foo4}
SELECT * FROM foo1, foo2, foo3; | {public.foo1,public.foo2,public.foo3}
SELECT * FROM foo1, foo2; | {public.foo1,public.foo2}
SELECT * FROM foo1; | {public.foo1}
SELECT pg_stat_monitor_reset(); |
SELECT query, relations from pg_stat_monitor ORDER BY query collate "C"; | {public.pg_stat_monitor*,pg_catalog.pg_database}
-------------------------------------------------------------------------+---------------------------------------------------
SELECT * FROM foo1 | {public.foo1}
SELECT * FROM foo1, foo2 | {public.foo1,public.foo2}
SELECT * FROM foo1, foo2, foo3 | {public.foo1,public.foo2,public.foo3}
SELECT * FROM foo1, foo2, foo3, foo4 | {public.foo1,public.foo2,public.foo3,public.foo4}
SELECT pg_stat_monitor_reset() |
SELECT query, relations from pg_stat_monitor ORDER BY query collate "C" | {public.pg_stat_monitor*,pg_catalog.pg_database}
(6 rows)
SELECT pg_stat_monitor_reset();
@ -90,13 +90,13 @@ SELECT * FROM sch1.foo1, sch2.foo2, sch3.foo3, sch4.foo4;
SELECT query, relations from pg_stat_monitor ORDER BY query collate "C";
query | relations
--------------------------------------------------------------------------+--------------------------------------------------
SELECT * FROM sch1.foo1, sch2.foo2, sch3.foo3, sch4.foo4; | {sch1.foo1,sch2.foo2,sch3.foo3,sch4.foo4}
SELECT * FROM sch1.foo1, sch2.foo2, sch3.foo3; | {sch1.foo1,sch2.foo2,sch3.foo3}
SELECT * FROM sch1.foo1, sch2.foo2; | {sch1.foo1,sch2.foo2}
SELECT * FROM sch1.foo1; | {sch1.foo1}
SELECT pg_stat_monitor_reset(); |
SELECT query, relations from pg_stat_monitor ORDER BY query collate "C"; | {public.pg_stat_monitor*,pg_catalog.pg_database}
-------------------------------------------------------------------------+--------------------------------------------------
SELECT * FROM sch1.foo1 | {sch1.foo1}
SELECT * FROM sch1.foo1, sch2.foo2 | {sch1.foo1,sch2.foo2}
SELECT * FROM sch1.foo1, sch2.foo2, sch3.foo3 | {sch1.foo1,sch2.foo2,sch3.foo3}
SELECT * FROM sch1.foo1, sch2.foo2, sch3.foo3, sch4.foo4 | {sch1.foo1,sch2.foo2,sch3.foo3,sch4.foo4}
SELECT pg_stat_monitor_reset() |
SELECT query, relations from pg_stat_monitor ORDER BY query collate "C" | {public.pg_stat_monitor*,pg_catalog.pg_database}
(6 rows)
SELECT pg_stat_monitor_reset();
@ -123,11 +123,11 @@ SELECT * FROM sch1.foo1, sch2.foo2, foo1, foo2;
SELECT query, relations from pg_stat_monitor ORDER BY query;
query | relations
--------------------------------------------------------------+--------------------------------------------------
SELECT * FROM sch1.foo1, foo1; | {sch1.foo1,public.foo1}
SELECT * FROM sch1.foo1, sch2.foo2, foo1, foo2; | {sch1.foo1,sch2.foo2,public.foo1,public.foo2}
SELECT pg_stat_monitor_reset(); |
SELECT query, relations from pg_stat_monitor ORDER BY query; | {public.pg_stat_monitor*,pg_catalog.pg_database}
-------------------------------------------------------------+--------------------------------------------------
SELECT * FROM sch1.foo1, foo1 | {sch1.foo1,public.foo1}
SELECT * FROM sch1.foo1, sch2.foo2, foo1, foo2 | {sch1.foo1,sch2.foo2,public.foo1,public.foo2}
SELECT pg_stat_monitor_reset() |
SELECT query, relations from pg_stat_monitor ORDER BY query | {public.pg_stat_monitor*,pg_catalog.pg_database}
(4 rows)
SELECT pg_stat_monitor_reset();
@ -169,13 +169,13 @@ SELECT * FROM v1,v2,v3,v4;
SELECT query, relations from pg_stat_monitor ORDER BY query collate "C";
query | relations
--------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------
SELECT * FROM v1,v2,v3,v4; | {public.v1*,public.foo1,public.v2*,public.foo2,public.v3*,public.foo3,public.v4*,public.foo4}
SELECT * FROM v1,v2,v3; | {public.v1*,public.foo1,public.v2*,public.foo2,public.v3*,public.foo3}
SELECT * FROM v1,v2; | {public.v1*,public.foo1,public.v2*,public.foo2}
SELECT * FROM v1; | {public.v1*,public.foo1}
SELECT pg_stat_monitor_reset(); |
SELECT query, relations from pg_stat_monitor ORDER BY query collate "C"; | {public.pg_stat_monitor*,pg_catalog.pg_database}
-------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------
SELECT * FROM v1 | {public.v1*,public.foo1}
SELECT * FROM v1,v2 | {public.v1*,public.foo1,public.v2*,public.foo2}
SELECT * FROM v1,v2,v3 | {public.v1*,public.foo1,public.v2*,public.foo2,public.v3*,public.foo3}
SELECT * FROM v1,v2,v3,v4 | {public.v1*,public.foo1,public.v2*,public.foo2,public.v3*,public.foo3,public.v4*,public.foo4}
SELECT pg_stat_monitor_reset() |
SELECT query, relations from pg_stat_monitor ORDER BY query collate "C" | {public.pg_stat_monitor*,pg_catalog.pg_database}
(6 rows)
SELECT pg_stat_monitor_reset();

View File

@ -1,7 +1,6 @@
CREATE EXTENSION pg_stat_monitor;
CREATE TABLE t1(a int);
CREATE TABLE t2(b int);
ERROR: relation "t2" already exists
INSERT INTO t1 VALUES(generate_series(1,1000));
INSERT INTO t2 VALUES(generate_series(1,5000));
SELECT pg_stat_monitor_reset();
@ -8542,12 +8541,12 @@ SELECt * FROM t2 WHERE b % 2 = 0;
SELECT query, rows_retrieved FROM pg_stat_monitor ORDER BY query COLLATE "C";
query | rows_retrieved
-------------------------------------------------------------------------------+----------------
------------------------------------------------------------------------------+----------------
SELECT * FROM t1 | 1000
SELECT * FROM t1 LIMIT $1 | 10
SELECT * FROM t1; | 1000
SELECT * FROM t2; | 5000
SELECT pg_stat_monitor_reset(); | 1
SELECT query, rows_retrieved FROM pg_stat_monitor ORDER BY query COLLATE "C"; | 0
SELECT * FROM t2 | 5000
SELECT pg_stat_monitor_reset() | 1
SELECT query, rows_retrieved FROM pg_stat_monitor ORDER BY query COLLATE "C" | 0
SELECt * FROM t2 WHERE b % $1 = $2 | 2500
(6 rows)
@ -8558,4 +8557,5 @@ SELECT pg_stat_monitor_reset();
(1 row)
DROP TABLE t1;
DROP TABLE t2;
DROP EXTENSION pg_stat_monitor;

View File

@ -8542,12 +8542,12 @@ SELECt * FROM t2 WHERE b % 2 = 0;
SELECT query, rows_retrieved FROM pg_stat_monitor ORDER BY query COLLATE "C";
query | rows_retrieved
-------------------------------------------------------------------------------+----------------
------------------------------------------------------------------------------+----------------
SELECT * FROM t1 | 1000
SELECT * FROM t1 LIMIT $1 | 10
SELECT * FROM t1; | 1000
SELECT b FROM t2 FOR UPDATE; | 5000
SELECT pg_stat_monitor_reset(); | 1
SELECT query, rows_retrieved FROM pg_stat_monitor ORDER BY query COLLATE "C"; | 0
SELECT b FROM t2 FOR UPDATE | 5000
SELECT pg_stat_monitor_reset() | 1
SELECT query, rows_retrieved FROM pg_stat_monitor ORDER BY query COLLATE "C" | 0
SELECt * FROM t2 WHERE b % $1 = $2 | 2500
(6 rows)

View File

@ -15,11 +15,11 @@ SELECT 1/0; -- divide by zero
ERROR: division by zero
SELECT query, state_code, state FROM pg_stat_monitor ORDER BY query COLLATE "C";
query | state_code | state
----------------------------------------------------------------------------------+------------+---------------------
---------------------------------------------------------------------------------+------------+---------------------
SELECT $1 | 3 | FINISHED
SELECT 1/0; | 4 | FINISHED WITH ERROR
SELECT pg_stat_monitor_reset(); | 3 | FINISHED
SELECT query, state_code, state FROM pg_stat_monitor ORDER BY query COLLATE "C"; | 2 | ACTIVE
SELECT pg_stat_monitor_reset() | 3 | FINISHED
SELECT query, state_code, state FROM pg_stat_monitor ORDER BY query COLLATE "C" | 2 | ACTIVE
(4 rows)
SELECT pg_stat_monitor_reset();

View File

@ -11,14 +11,54 @@ SELECT 1 AS num /* { "application", psql_app, "real_ip", 192.168.1.3) */;
1
(1 row)
SELECT query, comments FROM pg_stat_monitor ORDER BY query COLLATE "C";
query | comments
---------------------------------------------------------------------------+----------
SELECT $1 AS num /* { "application", psql_app, "real_ip", 192.168.1.3) */ |
SELECT pg_stat_monitor_reset() |
SELECT query, comments FROM pg_stat_monitor ORDER BY query COLLATE "C" |
(3 rows)
ALTER SYSTEM SET pg_stat_monitor.extract_comments TO 'yes';
SELECT pg_reload_conf();
pg_reload_conf
----------------
t
(1 row)
select pg_sleep(1);
pg_sleep
----------
(1 row)
SELECT pg_stat_monitor_reset();
pg_stat_monitor_reset
-----------------------
(1 row)
SELECT 1 AS num /* { "application", psql_app, "real_ip", 192.168.1.3) */;
num
-----
1
(1 row)
SELECT query, comments FROM pg_stat_monitor ORDER BY query COLLATE "C";
query | comments
---------------------------------------------------------------------------+----------------------------------------------------------
SELECT $1 AS num /* { "application", psql_app, "real_ip", 192.168.1.3) */ | /* { "application", psql_app, "real_ip", 192.168.1.3) */
SELECT pg_stat_monitor_reset(); |
SELECT query, comments FROM pg_stat_monitor ORDER BY query COLLATE "C"; |
SELECT pg_stat_monitor_reset() |
SELECT query, comments FROM pg_stat_monitor ORDER BY query COLLATE "C" |
(3 rows)
ALTER SYSTEM SET pg_stat_monitor.extract_comments TO 'no';
SELECT pg_reload_conf();
pg_reload_conf
----------------
t
(1 row)
SELECT pg_stat_monitor_reset();
pg_stat_monitor_reset
-----------------------

View File

@ -24,24 +24,92 @@ SELECT add2(1,2);
SELECT query, top_query FROM pg_stat_monitor ORDER BY query COLLATE "C";
query | top_query
--------------------------------------------------------------------------+--------------------
-------------------------------------------------------------------------+-----------
CREATE OR REPLACE FUNCTION add(int, int) RETURNS INTEGER AS +|
$$ +|
BEGIN +|
return (select $1 + $2); +|
END; $$ language plpgsql; |
END; $$ language plpgsql |
CREATE OR REPLACE function add2(int, int) RETURNS int as +|
$$ +|
BEGIN +|
return add($1,$2); +|
END; +|
$$ language plpgsql; |
$$ language plpgsql |
SELECT add2($1,$2) |
SELECT pg_stat_monitor_reset() |
SELECT query, top_query FROM pg_stat_monitor ORDER BY query COLLATE "C" |
(5 rows)
ALTER SYSTEM SET pg_stat_monitor.track TO 'all';
SELECT pg_reload_conf();
pg_reload_conf
----------------
t
(1 row)
SELECT pg_sleep(1);
pg_sleep
----------
(1 row)
SELECT pg_stat_monitor_reset();
pg_stat_monitor_reset
-----------------------
(1 row)
CREATE OR REPLACE FUNCTION add(int, int) RETURNS INTEGER AS
$$
BEGIN
return (select $1 + $2);
END; $$ language plpgsql;
CREATE OR REPLACE function add2(int, int) RETURNS int as
$$
BEGIN
return add($1,$2);
END;
$$ language plpgsql;
SELECT add2(1,2);
add2
------
3
(1 row)
SELECT query, top_query FROM pg_stat_monitor ORDER BY query COLLATE "C";
query | top_query
-------------------------------------------------------------------------+--------------------
CREATE OR REPLACE FUNCTION add(int, int) RETURNS INTEGER AS +|
$$ +|
BEGIN +|
return (select $1 + $2); +|
END; $$ language plpgsql |
CREATE OR REPLACE function add2(int, int) RETURNS int as +|
$$ +|
BEGIN +|
return add($1,$2); +|
END; +|
$$ language plpgsql |
SELECT (select $1 + $2) | SELECT add2($1,$2)
SELECT add2($1,$2) |
SELECT pg_stat_monitor_reset(); |
SELECT query, top_query FROM pg_stat_monitor ORDER BY query COLLATE "C"; |
SELECT pg_stat_monitor_reset() |
SELECT query, top_query FROM pg_stat_monitor ORDER BY query COLLATE "C" |
(6 rows)
ALTER SYSTEM SET pg_stat_monitor.track TO 'top';
SELECT pg_reload_conf();
pg_reload_conf
----------------
t
(1 row)
SELECT pg_sleep(1);
pg_sleep
----------
(1 row)
SELECT pg_stat_monitor_reset();
pg_stat_monitor_reset
-----------------------

View File

@ -24,24 +24,92 @@ SELECT add2(1,2);
SELECT query, top_query FROM pg_stat_monitor ORDER BY query COLLATE "C";
query | top_query
--------------------------------------------------------------------------+--------------------
(select $1 + $2) | SELECT add2($1,$2)
-------------------------------------------------------------------------+-----------
CREATE OR REPLACE FUNCTION add(int, int) RETURNS INTEGER AS +|
$$ +|
BEGIN +|
return (select $1 + $2); +|
END; $$ language plpgsql; |
END; $$ language plpgsql |
CREATE OR REPLACE function add2(int, int) RETURNS int as +|
$$ +|
BEGIN +|
return add($1,$2); +|
END; +|
$$ language plpgsql; |
$$ language plpgsql |
SELECT add2($1,$2) |
SELECT pg_stat_monitor_reset(); |
SELECT query, top_query FROM pg_stat_monitor ORDER BY query COLLATE "C"; |
SELECT pg_stat_monitor_reset() |
SELECT query, top_query FROM pg_stat_monitor ORDER BY query COLLATE "C" |
(5 rows)
ALTER SYSTEM SET pg_stat_monitor.track TO 'all';
SELECT pg_reload_conf();
pg_reload_conf
----------------
t
(1 row)
SELECT pg_sleep(1);
pg_sleep
----------
(1 row)
SELECT pg_stat_monitor_reset();
pg_stat_monitor_reset
-----------------------
(1 row)
CREATE OR REPLACE FUNCTION add(int, int) RETURNS INTEGER AS
$$
BEGIN
return (select $1 + $2);
END; $$ language plpgsql;
CREATE OR REPLACE function add2(int, int) RETURNS int as
$$
BEGIN
return add($1,$2);
END;
$$ language plpgsql;
SELECT add2(1,2);
add2
------
3
(1 row)
SELECT query, top_query FROM pg_stat_monitor ORDER BY query COLLATE "C";
query | top_query
-------------------------------------------------------------------------+--------------------
(select $1 + $2) | SELECT add2($1,$2)
CREATE OR REPLACE FUNCTION add(int, int) RETURNS INTEGER AS +|
$$ +|
BEGIN +|
return (select $1 + $2); +|
END; $$ language plpgsql |
CREATE OR REPLACE function add2(int, int) RETURNS int as +|
$$ +|
BEGIN +|
return add($1,$2); +|
END; +|
$$ language plpgsql |
SELECT add2($1,$2) |
SELECT pg_stat_monitor_reset() |
SELECT query, top_query FROM pg_stat_monitor ORDER BY query COLLATE "C" |
(6 rows)
ALTER SYSTEM SET pg_stat_monitor.track TO 'top';
SELECT pg_reload_conf();
pg_reload_conf
----------------
t
(1 row)
SELECT pg_sleep(1);
pg_sleep
----------
(1 row)
SELECT pg_stat_monitor_reset();
pg_stat_monitor_reset
-----------------------

View File

@ -2,7 +2,7 @@ CREATE EXTENSION pg_stat_monitor;
SELECT pg_stat_monitor_version();
pg_stat_monitor_version
-------------------------
1.0.0 - Beta 2
1.0.0-rc.1
(1 row)
DROP EXTENSION pg_stat_monitor;

View File

@ -10,6 +10,7 @@ DELETE FROM t1;
SELECT b FROM t2 FOR UPDATE;
TRUNCATE t1;
DROP TABLE t1;
DROP TABLE t2;
SELECT query, cmd_type, cmd_type_text FROM pg_stat_monitor ORDER BY query COLLATE "C";
SELECT pg_stat_monitor_reset();

View File

@ -16,4 +16,5 @@ SELECT query, rows_retrieved FROM pg_stat_monitor ORDER BY query COLLATE "C";
SELECT pg_stat_monitor_reset();
DROP TABLE t1;
DROP TABLE t2;
DROP EXTENSION pg_stat_monitor;

View File

@ -2,5 +2,13 @@ CREATE EXTENSION pg_stat_monitor;
SELECT pg_stat_monitor_reset();
SELECT 1 AS num /* { "application", psql_app, "real_ip", 192.168.1.3) */;
SELECT query, comments FROM pg_stat_monitor ORDER BY query COLLATE "C";
ALTER SYSTEM SET pg_stat_monitor.extract_comments TO 'yes';
SELECT pg_reload_conf();
select pg_sleep(1);
SELECT pg_stat_monitor_reset();
SELECT 1 AS num /* { "application", psql_app, "real_ip", 192.168.1.3) */;
SELECT query, comments FROM pg_stat_monitor ORDER BY query COLLATE "C";
ALTER SYSTEM SET pg_stat_monitor.extract_comments TO 'no';
SELECT pg_reload_conf();
SELECT pg_stat_monitor_reset();
DROP EXTENSION pg_stat_monitor;

View File

@ -1,5 +1,6 @@
CREATE EXTENSION pg_stat_monitor;
SELECT pg_stat_monitor_reset();
CREATE OR REPLACE FUNCTION add(int, int) RETURNS INTEGER AS
$$
BEGIN
@ -15,5 +16,30 @@ $$ language plpgsql;
SELECT add2(1,2);
SELECT query, top_query FROM pg_stat_monitor ORDER BY query COLLATE "C";
ALTER SYSTEM SET pg_stat_monitor.track TO 'all';
SELECT pg_reload_conf();
SELECT pg_sleep(1);
SELECT pg_stat_monitor_reset();
CREATE OR REPLACE FUNCTION add(int, int) RETURNS INTEGER AS
$$
BEGIN
return (select $1 + $2);
END; $$ language plpgsql;
CREATE OR REPLACE function add2(int, int) RETURNS int as
$$
BEGIN
return add($1,$2);
END;
$$ language plpgsql;
SELECT add2(1,2);
SELECT query, top_query FROM pg_stat_monitor ORDER BY query COLLATE "C";
ALTER SYSTEM SET pg_stat_monitor.track TO 'top';
SELECT pg_reload_conf();
SELECT pg_sleep(1);
SELECT pg_stat_monitor_reset();
DROP EXTENSION pg_stat_monitor;