├── .github └── workflows │ ├── powa_archivist.yml │ ├── powa_archivist_git.yml │ └── regression.yml ├── .gitignore ├── CHANGELOG.md ├── CONTRIBUTORS.md ├── LICENSE ├── META.json ├── Makefile ├── README.rst ├── debian ├── changelog ├── control ├── control.in ├── copyright ├── pgversions ├── rules ├── source │ └── format ├── tests │ ├── control │ ├── control.in │ └── installcheck └── watch ├── expected └── 01_basic.out ├── pg_stat_kcache--2.1.0--2.1.1.sql ├── pg_stat_kcache--2.1.0.sql ├── pg_stat_kcache--2.1.1--2.1.2.sql ├── pg_stat_kcache--2.1.1.sql ├── pg_stat_kcache--2.1.2--2.1.3.sql ├── pg_stat_kcache--2.1.2.sql ├── pg_stat_kcache--2.1.3--2.2.0.sql ├── pg_stat_kcache--2.1.3.sql ├── pg_stat_kcache--2.2.0--2.2.1.sql ├── pg_stat_kcache--2.2.0.sql ├── pg_stat_kcache--2.2.1--2.2.2.sql ├── pg_stat_kcache--2.2.1.sql ├── pg_stat_kcache--2.2.2--2.2.3.sql ├── pg_stat_kcache--2.2.2.sql ├── pg_stat_kcache--2.2.3--2.3.0.sql ├── pg_stat_kcache--2.2.3.sql ├── pg_stat_kcache--2.3.0.sql ├── pg_stat_kcache.c ├── pg_stat_kcache.control ├── pg_stat_kcache.h └── test └── sql └── 01_basic.sql /.github/workflows/powa_archivist.yml: -------------------------------------------------------------------------------- 1 | name: Trigger build and push of powa-archivist image 2 | 3 | on: 4 | push: 5 | # https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#filter-pattern-cheat-sheet 6 | tags: 7 | - 'REL*' 8 | 9 | env: 10 | TARGET_REPO: "powa-podman" 11 | EVENT_TYPE: "powa-archivist" 12 | 13 | jobs: 14 | trigger_build: 15 | name: Trigger build and push of powa-archivist in powa-podman repo 16 | runs-on: ubuntu-latest 17 | steps: 18 | - name: Trigger the powa-archivist-git repository dispatch 19 | run: | 20 | # Set variables 21 | org="${{ github.repository_owner }}" 22 | repo="${{ env.TARGET_REPO }}" 23 | event_type="${{ env.EVENT_TYPE }}" 24 | 25 | curl -L \ 26 | -X POST \ 27 | -H "Accept: application/vnd.github+json" \ 28 | -H "Authorization: Bearer ${{ secrets.DISPATCH_TOKEN }}" \ 29 | -H "X-GitHub-Api-Version: 2022-11-28" \ 30 | https://api.github.com/repos/${org}/${repo}/dispatches \ 31 | -d "{\"event_type\": \"${event_type}\"}" 32 | -------------------------------------------------------------------------------- /.github/workflows/powa_archivist_git.yml: -------------------------------------------------------------------------------- 1 | name: Trigger build and push of powa-archivist-git image 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | 7 | env: 8 | TARGET_REPO: "powa-podman" 9 | EVENT_TYPE: "powa-archivist-git" 10 | 11 | jobs: 12 | trigger_build: 13 | name: Trigger build and push of powa-archivist-git in powa-podman repo 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Trigger the powa-archivist-git repository dispatch 17 | run: | 18 | # Set variables 19 | org="${{ github.repository_owner }}" 20 | repo="${{ env.TARGET_REPO }}" 21 | event_type="${{ env.EVENT_TYPE }}" 22 | 23 | curl -L \ 24 | -X POST \ 25 | -H "Accept: application/vnd.github+json" \ 26 | -H "Authorization: Bearer ${{ secrets.DISPATCH_TOKEN }}" \ 27 | -H "X-GitHub-Api-Version: 2022-11-28" \ 28 | https://api.github.com/repos/${org}/${repo}/dispatches \ 29 | -d "{\"event_type\": \"${event_type}\"}" 30 | -------------------------------------------------------------------------------- /.github/workflows/regression.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches: 9 | - master 10 | 11 | jobs: 12 | build: 13 | runs-on: ubuntu-latest 14 | 15 | defaults: 16 | run: 17 | shell: sh 18 | 19 | strategy: 20 | matrix: 21 | pgversion: 22 | - 9.4 23 | - 9.5 24 | - 9.6 25 | - 10 26 | - 11 27 | - 12 28 | - 13 29 | - 14 30 | - 15 31 | - 16 32 | - 17 33 | 34 | env: 35 | PGVERSION: ${{ matrix.pgversion }} 36 | 37 | steps: 38 | - name: checkout 39 | uses: actions/checkout@v2 40 | 41 | - name: install pg 42 | run: | 43 | sudo /usr/share/postgresql-common/pgdg/apt.postgresql.org.sh -v $PGVERSION -p -i 44 | sudo -u postgres createuser -s "$USER" 45 | 46 | - name: build 47 | run: | 48 | make PROFILE="-Werror" 49 | sudo -E make install 50 | 51 | - name: test 52 | run: | 53 | sudo pg_conftool $PGVERSION main set shared_preload_libraries pg_stat_statements,pg_stat_kcache 54 | sudo service postgresql restart 55 | make installcheck 56 | 57 | - name: show regression diffs 58 | if: ${{ failure() }} 59 | run: | 60 | cat regression.diffs 61 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.a 3 | *.so 4 | *.pc 5 | *~ 6 | pg_stat_kcache-*.zip 7 | results/ 8 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 2.3.0 (2024-09-17) 2 | 3 | **New features**: 4 | 5 | - Track entry creation timestamp (Julien Rouhaud, thanks to Andrei Zubkov for 6 | the feature request) 7 | 8 | **Bugfix**: 9 | 10 | - Fix some issues with tracking nesting level (Julien Rouhaud) 11 | - Fix the pgsk_counters_hook declaration (Julien Rouhaud) 12 | 13 | **Miscellaneous**: 14 | 15 | - Add postgres 17 compatibility (Georgy Shelkovy) 16 | 17 | ## 2.2.3 (2024-01-24) 18 | 19 | **Miscellaneous**: 20 | 21 | - Improve performance for workloads with a high number of clients (Julien 22 | Rouhaud, thanks to Vitaliy Kukharik for the report) 23 | 24 | ## 2.2.2 (2023-08-01) 25 | 26 | **Miscellaneous**: 27 | 28 | - Add postgres 16 compatibility (Julien Rouhaud, Christoph Berg, Japin Li) 29 | - Add CI (Christoph Berg) 30 | - Recommend installation from PGDG repository rather than compiling the 31 | extension (Julien Rouhaud) 32 | 33 | ## 2.2.1 (2022-05-16) 34 | 35 | **Bugfix**: 36 | 37 | - Fix memory allocation on postgres 12 and above (Julien Rouhaud) 38 | **New features**: 39 | 40 | - Expose some structures and add a counters hook so other extensions can 41 | extend this extension's metrics (Sviatoslav Ermilin) 42 | 43 | **Miscellaneous**: 44 | 45 | - Various improvements to the debian packaging (Christoph Berg) 46 | - Add compatibility with postgres 15 (Julien Rouhaud) 47 | - Improve extension update documentation (Julien Rouhaud) 48 | 49 | ## 2.2.0 (2020-12-10) 50 | 51 | **New features**: 52 | 53 | - Add pg_stat_kcache.track option, similar to pg_stat_statements (Julien 54 | Rouhaud and github user mikecaat) 55 | - Add pg_stat_kcache.track_planning, similar to pg_stat_statements, to 56 | track usge during planning, and maintain 2 sets of counters, depending on 57 | whether it was during planning or execution. Requires PostgreSQL 13 or 58 | above (Julien Rouhaud, github user mikecaat) 59 | - Add a new "top" column, and accumulate resource usage in different entries 60 | depending on whether queries are executed at top level or at nested 61 | statements. (github user mikecaat and Julien Rouhaud) 62 | 63 | ## 2.1.3 (2020-07-16) 64 | 65 | **Bugfix**: 66 | 67 | - Fix memory corruption introduced in v2.1.2 that can cause a PostgreSQL 68 | crash (Julien Rouhaud, thanks to github user tbe, Nikolay Samokhvalov and 69 | Andreas Seltenreich for reporting the issue and the extensive checking) 70 | 71 | ## 2.1.2 (2020-07-08) 72 | 73 | **Bugfix**: 74 | 75 | - Accumulate counters for parallel workers too (Julien Rouhaud, thanks to 76 | Atsushi Torikoshi for the report) 77 | 78 | ## 2.1.1 (2018-07-28) 79 | 80 | **Bugfix**: 81 | 82 | - Fix usage increase, used to keep the most frequent entries in memory 83 | (Julien Rouhaud) 84 | 85 | **Miscellaneous**: 86 | 87 | - Allow PG_CONFIG value to be found on command-line (edechaux) 88 | - Warn users about incorrect GUC (Julien Rouhaud) 89 | - Add debian packaging (Julien Rouhaud) 90 | 91 | ## 2.1.0 (2018-07-17) 92 | 93 | **NOTE**: This version requires a change to the on-disk format. After 94 | installing the new version restarting PostgreSQL, any previously accumulated 95 | statistics will be reset. 96 | 97 | - Add support for architecture that don't provide getrusage(2), such as 98 | windows. Only user time and system time will be available on such 99 | platforms (Julien Rouhaud). 100 | - Expose more fields of getrusage(2). Depending on the platform, some of 101 | these fields are not maintained (Julien Rouhaud). 102 | - Add a workaround for sampling problems with getrusage(), new parameter 103 | pg_stat_kcache.linux_hz is added. By default, this parameter is discovered 104 | at server startup (Ronan Dunklau). 105 | - Add compatibility with PostgreSQL 11 (Thomas Reiss) 106 | - Fix issue when concurrently created entries for the same user, db and 107 | queryid could lost some execution counters (Mael Rimbault) 108 | - Do not install docs anymore (Ronan Dunklau) 109 | 110 | ## 2.0.3 (2016-10-03) 111 | - Add PG 9.6 compatibility 112 | - Fix issues in shared memory estimation, which could prevent starting 113 | postgres or reduce the amount of possible locks (thanks to Jean-Sébastien 114 | BACQ for the report) 115 | - Add hint of possible reasons pgss.max could not be retrieved, which could 116 | prevent starting postgres 117 | 118 | ## 2.0.2 (2015-03-17) 119 | 120 | - Fix another bug with 32 bits builds (thanks to Alain Delorme for reporting it) 121 | 122 | ## 2.0.1 (2015-03-16) 123 | 124 | - Fix a bug with 32 bits builds (thanks to Alain Delorme for reporting it) 125 | 126 | ## 2.0 (2015-01-30) 127 | 128 | - Handle stats per database, user, query 129 | 130 | ## 1.0 (2014-02-26) 131 | 132 | - Initial release 133 | -------------------------------------------------------------------------------- /CONTRIBUTORS.md: -------------------------------------------------------------------------------- 1 | * Thomas Reiss 2 | * Julien Rouhaud 3 | * Alain Delorme 4 | * Guillaume Lelarge 5 | * Jean-Sébastien Bacq 6 | * Maël Rimbault 7 | * edechaux 8 | * Felix Geisendörfer 9 | * github user atorik 10 | * github user tbe 11 | * github user mikecaat 12 | * github user munakoiso 13 | * Christoph Berg 14 | * Sviatoslav Ermilin 15 | * Vitaliy Kukharik 16 | * Georgy Shelkovy 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014-2017, Dalibo 2 | Copyright (c) 2018-2023, The PoWA-team 3 | 4 | Permission to use, copy, modify, and distribute this software and its 5 | documentation for any purpose, without fee, and without a written agreement 6 | is hereby granted, provided that the above copyright notice and this 7 | paragraph and the following two paragraphs appear in all copies. 8 | 9 | IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR DIRECT, 10 | INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST 11 | PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 12 | THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 13 | 14 | THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT 15 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 16 | PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND 17 | THE COPYRIGHT HOLDER HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT, 18 | UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 19 | -------------------------------------------------------------------------------- /META.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pg_stat_kcache", 3 | "abstract": "An extension gathering CPU and disk acess statistics", 4 | "version": "__VERSION__", 5 | "maintainer": "Julien Rouhaud ", 6 | "license": "postgresql", 7 | "release_status": "stable", 8 | "provides": { 9 | "pg_stat_kcache": { 10 | "abstract": "An extension gathering CPU and disk acess statistics", 11 | "file": "pg_stat_kcache.sql", 12 | "docfile": "README.rst", 13 | "version": "__VERSION__" 14 | } 15 | }, 16 | "resources": { 17 | "bugtracker": { 18 | "web": "http://github.com/powa-team/pg_stat_kcache/issues/" 19 | }, 20 | "repository": { 21 | "url": "git://github.com/powa-team/pg_stat_kcache.git", 22 | "web": "http://github.com/powa-team/pg_stat_kcache/", 23 | "type": "git" 24 | } 25 | }, 26 | "prereqs": { 27 | "runtime": { 28 | "requires": { 29 | "PostgreSQL": "9.4.0" 30 | } 31 | } 32 | }, 33 | "generated_by": "Julien Rouhaud", 34 | "meta-spec": { 35 | "version": "1.0.0", 36 | "url": "http://pgxn.org/meta/spec.txt" 37 | }, 38 | "tags": [ 39 | "monitoring", 40 | "CPU", 41 | "disk" 42 | ] 43 | } 44 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | EXTENSION = pg_stat_kcache 2 | EXTVERSION = $(shell grep default_version $(EXTENSION).control | sed -e "s/default_version[[:space:]]*=[[:space:]]*'\([^']*\)'/\1/") 3 | TESTS = $(wildcard test/sql/*.sql) 4 | REGRESS = $(patsubst test/sql/%.sql,%,$(TESTS)) 5 | REGRESS_OPTS = --inputdir=test 6 | 7 | PG_CONFIG ?= pg_config 8 | 9 | MODULE_big = pg_stat_kcache 10 | OBJS = pg_stat_kcache.o 11 | 12 | all: 13 | 14 | release-zip: all 15 | git archive --format zip --prefix=pg_stat_kcache-${EXTVERSION}/ --output ./pg_stat_kcache-${EXTVERSION}.zip HEAD 16 | unzip ./pg_stat_kcache-$(EXTVERSION).zip 17 | rm ./pg_stat_kcache-$(EXTVERSION).zip 18 | rm ./pg_stat_kcache-$(EXTVERSION)/.gitignore 19 | sed -i -e "s/__VERSION__/$(EXTVERSION)/g" ./pg_stat_kcache-$(EXTVERSION)/META.json 20 | zip -r ./pg_stat_kcache-$(EXTVERSION).zip ./pg_stat_kcache-$(EXTVERSION)/ 21 | rm ./pg_stat_kcache-$(EXTVERSION) -rf 22 | 23 | 24 | DATA = $(wildcard *--*.sql) 25 | PGXS := $(shell $(PG_CONFIG) --pgxs) 26 | include $(PGXS) 27 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | pg_stat_kcache 2 | ============== 3 | 4 | Features 5 | -------- 6 | 7 | Gathers statistics about real reads and writes done by the filesystem layer. 8 | It is provided in the form of an extension for PostgreSQL >= 9.4., and requires 9 | pg_stat_statements extension to be installed. PostgreSQL 9.4 or more is 10 | required as previous version of provided pg_stat_statements didn't expose the 11 | queryid field. 12 | 13 | Installation 14 | ============ 15 | 16 | From PGDG repositories 17 | ---------------------- 18 | 19 | If you installed PostgreSQL from the PGDG repositories (either `APT on 20 | Debian/Ubuntu ` or `YUM on RHEL/Rocky 21 | Tue, 17 Sep 2024 12:00:44 +0800 6 | 7 | pg-stat-kcache (2.2.3-1) unstable; urgency=medium 8 | 9 | * New upstream version. 10 | 11 | -- Julien Rouhaud Wed, 24 Jan 2024 17:29:41 +0800 12 | 13 | pg-stat-kcache (2.2.2-2) unstable; urgency=medium 14 | 15 | * Team upload. 16 | * Upload for PostgreSQL 16. 17 | * Use ${postgresql:Depends}. 18 | 19 | -- Christoph Berg Sun, 17 Sep 2023 21:02:35 +0200 20 | 21 | pg-stat-kcache (2.2.2-1) unstable; urgency=medium 22 | 23 | * Team upload. 24 | * New upstream version with PG16 support. 25 | 26 | -- Christoph Berg Wed, 02 Aug 2023 16:10:59 +0200 27 | 28 | pg-stat-kcache (2.2.1-2) unstable; urgency=medium 29 | 30 | * Team upload for PostgreSQL 15. 31 | * debian/watch: Look at GitHub tags instead of releases. 32 | 33 | -- Christoph Berg Fri, 21 Oct 2022 11:03:01 +0200 34 | 35 | pg-stat-kcache (2.2.1-1) unstable; urgency=medium 36 | 37 | * New upstream version. 38 | 39 | -- Julien Rouhaud Mon, 16 May 2022 14:03:03 +0800 40 | 41 | pg-stat-kcache (2.2.0-2) unstable; urgency=medium 42 | 43 | * Upload for PostgreSQL 14. 44 | 45 | -- Christoph Berg Fri, 15 Oct 2021 15:44:51 +0200 46 | 47 | pg-stat-kcache (2.2.0-1) unstable; urgency=medium 48 | 49 | * New upstream version. 50 | 51 | -- Julien Rouhaud Wed, 09 Dec 2020 22:01:38 +0800 52 | 53 | pg-stat-kcache (2.1.3-2) unstable; urgency=medium 54 | 55 | * Team upload for PostgreSQL 13. 56 | * Source format 3.0 (quilt). 57 | * Use dh --with pgxs_loop. 58 | * R³: no. 59 | * DH 13. 60 | * debian/tests: Use 'make' instead of postgresql-server-dev-all. 61 | 62 | -- Christoph Berg Mon, 19 Oct 2020 11:49:40 +0200 63 | 64 | pg-stat-kcache (2.1.3-1) unstable; urgency=medium 65 | 66 | * New upstream version. 67 | 68 | -- Julien Rouhaud Thu, 16 Jul 2020 15:12:23 +0000 69 | 70 | pg-stat-kcache (2.1.2-1) unstable; urgency=medium 71 | 72 | * New upstream version. 73 | 74 | -- Julien Rouhaud Wed, 08 Jul 2020 16:12:51 +0000 75 | 76 | pg-stat-kcache (2.1.1-3) unstable; urgency=medium 77 | 78 | * Team upload for PostgreSQL 12. 79 | 80 | -- Christoph Berg Tue, 29 Oct 2019 13:48:37 +0100 81 | 82 | pg-stat-kcache (2.1.1-2) unstable; urgency=medium 83 | 84 | * Team upload for PostgreSQL 11. 85 | 86 | -- Christoph Berg Fri, 12 Oct 2018 13:17:38 +0200 87 | 88 | pg-stat-kcache (2.1.1-1) unstable; urgency=low 89 | 90 | * Initial release. 91 | 92 | -- Julien Rouhaud Sun, 22 Jul 2018 19:19:36 +0100 93 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: pg-stat-kcache 2 | Section: database 3 | Priority: optional 4 | Maintainer: Julien Rouhaud 5 | Standards-Version: 4.6.2 6 | Rules-Requires-Root: no 7 | Build-Depends: debhelper-compat (= 13), postgresql-all (>= 217~) 8 | Homepage: https://powa.readthedocs.io/ 9 | Vcs-Browser: https://github.com/powa-team/pg_stat_kcache 10 | Vcs-Git: https://github.com/powa-team/pg_stat_kcache.git 11 | 12 | Package: postgresql-16-pg-stat-kcache 13 | Architecture: any 14 | Depends: ${misc:Depends}, ${shlibs:Depends}, ${postgresql:Depends}, 15 | postgresql-contrib-16 16 | Description: PostgreSQL extension to gather per-query kernel statistics. 17 | Statistics gathered are reads and writes done out of the operating system 18 | cache, user and system CPU usage, context switches and all the other 19 | meaningful metrics returned by getrusage(2). All those counters are 20 | aggregated per postgres role, database and normalized query identifier. 21 | -------------------------------------------------------------------------------- /debian/control.in: -------------------------------------------------------------------------------- 1 | Source: pg-stat-kcache 2 | Section: database 3 | Priority: optional 4 | Maintainer: Julien Rouhaud 5 | Standards-Version: 4.6.2 6 | Rules-Requires-Root: no 7 | Build-Depends: debhelper-compat (= 13), postgresql-all (>= 217~) 8 | Homepage: https://powa.readthedocs.io/ 9 | Vcs-Browser: https://github.com/powa-team/pg_stat_kcache 10 | Vcs-Git: https://github.com/powa-team/pg_stat_kcache.git 11 | 12 | Package: postgresql-PGVERSION-pg-stat-kcache 13 | Architecture: any 14 | Depends: ${misc:Depends}, ${shlibs:Depends}, ${postgresql:Depends}, 15 | postgresql-contrib-PGVERSION 16 | Description: PostgreSQL extension to gather per-query kernel statistics. 17 | Statistics gathered are reads and writes done out of the operating system 18 | cache, user and system CPU usage, context switches and all the other 19 | meaningful metrics returned by getrusage(2). All those counters are 20 | aggregated per postgres role, database and normalized query identifier. 21 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | Portions Copyright (c) 2014-2017, Dalibo 2 | Portions Copyright (c) 2018-2024, The PoWA-team 3 | 4 | Permission to use, copy, modify, and distribute this software and its 5 | documentation for any purpose, without fee, and without a written agreement 6 | is hereby granted, provided that the above copyright notice and this 7 | paragraph and the following two paragraphs appear in all copies. 8 | 9 | IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR DIRECT, 10 | INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST 11 | PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 12 | THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 13 | 14 | THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT 15 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 16 | PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND 17 | THE COPYRIGHT HOLDER HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT, 18 | UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 19 | 20 | Contributors to pg_stat_kcache: 21 | 22 | * Thomas Reiss 23 | * Julien Rouhaud 24 | * Guillaume Lelarge 25 | * Ronan Dunklau 26 | * Maël Rimbault 27 | * Alain Delorme 28 | * Jean-Sébastien Bacq 29 | * edechaux 30 | * Felix Geisendörfer 31 | * github user atorik 32 | * github user tbe 33 | * github user mikecaat 34 | -------------------------------------------------------------------------------- /debian/pgversions: -------------------------------------------------------------------------------- 1 | 9.4+ 2 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | 3 | override_dh_pgxs_test: 4 | pg_buildext -o "shared_preload_libraries=pg_stat_statements,pg_stat_kcache" installcheck . . postgresql-%v-pg-stat-kcache 5 | 6 | override_dh_installdocs: 7 | dh_installdocs --all CONTRIBUTORS.md README.rst 8 | rm -rvf debian/*/usr/share/doc/postgresql-doc-* 9 | 10 | %: 11 | dh $@ --with pgxs_loop 12 | -------------------------------------------------------------------------------- /debian/source/format: -------------------------------------------------------------------------------- 1 | 3.0 (quilt) 2 | -------------------------------------------------------------------------------- /debian/tests/control: -------------------------------------------------------------------------------- 1 | Depends: @, postgresql-contrib-16, make 2 | Tests: installcheck 3 | Restrictions: allow-stderr 4 | -------------------------------------------------------------------------------- /debian/tests/control.in: -------------------------------------------------------------------------------- 1 | Depends: @, postgresql-contrib-PGVERSION, make 2 | Tests: installcheck 3 | Restrictions: allow-stderr 4 | -------------------------------------------------------------------------------- /debian/tests/installcheck: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -eu 4 | 5 | pg_buildext -o "shared_preload_libraries=pg_stat_statements,pg_stat_kcache" installcheck 6 | -------------------------------------------------------------------------------- /debian/watch: -------------------------------------------------------------------------------- 1 | version=4 2 | opts="uversionmangle=s/_/./g" \ 3 | https://github.com/powa-team/pg_stat_kcache/tags .*/REL(.*).tar.gz 4 | -------------------------------------------------------------------------------- /expected/01_basic.out: -------------------------------------------------------------------------------- 1 | CREATE EXTENSION pg_stat_statements; 2 | CREATE EXTENSION pg_stat_kcache; 3 | -- first make sure that catcache is loaded to avoid physical reads 4 | SELECT count(*) >= 0 FROM pg_stat_kcache; 5 | ?column? 6 | ---------- 7 | t 8 | (1 row) 9 | 10 | SELECT pg_stat_kcache_reset(); 11 | pg_stat_kcache_reset 12 | ---------------------- 13 | 14 | (1 row) 15 | 16 | -- dummy query 17 | SELECT 1 AS dummy; 18 | dummy 19 | ------- 20 | 1 21 | (1 row) 22 | 23 | SELECT count(*) FROM pg_stat_kcache WHERE datname = current_database(); 24 | count 25 | ------- 26 | 1 27 | (1 row) 28 | 29 | SELECT count(*) FROM pg_stat_kcache_detail WHERE datname = current_database() AND (query = 'SELECT $1 AS dummy' OR query = 'SELECT ? AS dummy;'); 30 | count 31 | ------- 32 | 1 33 | (1 row) 34 | 35 | SELECT exec_reads, exec_reads_blks, exec_writes, exec_writes_blks 36 | FROM pg_stat_kcache_detail 37 | WHERE datname = current_database() 38 | AND (query = 'SELECT $1 AS dummy' OR query = 'SELECT ? AS dummy;'); 39 | exec_reads | exec_reads_blks | exec_writes | exec_writes_blks 40 | ------------+-----------------+-------------+------------------ 41 | 0 | 0 | 0 | 0 42 | (1 row) 43 | 44 | -- dummy table 45 | CREATE TABLE test AS SELECT i FROM generate_series(1, 1000) i; 46 | -- dummy query again 47 | SELECT count(*) FROM test; 48 | count 49 | ------- 50 | 1000 51 | (1 row) 52 | 53 | SELECT exec_user_time + exec_system_time > 0 AS cpu_time_ok 54 | FROM pg_stat_kcache_detail 55 | WHERE datname = current_database() 56 | AND query LIKE 'SELECT count(*) FROM test%'; 57 | cpu_time_ok 58 | ------------- 59 | t 60 | (1 row) 61 | 62 | -- dummy nested query 63 | SET pg_stat_statements.track = 'all'; 64 | SET pg_stat_statements.track_planning = TRUE; 65 | SET pg_stat_kcache.track = 'all'; 66 | SET pg_stat_kcache.track_planning = TRUE; 67 | SELECT pg_stat_kcache_reset(); 68 | pg_stat_kcache_reset 69 | ---------------------- 70 | 71 | (1 row) 72 | 73 | CREATE OR REPLACE FUNCTION plpgsql_nested() 74 | RETURNS void AS $$ 75 | BEGIN 76 | PERFORM i::text as str from generate_series(1, 100) as i; 77 | PERFORM md5(i::text) as str from generate_series(1, 100) as i; 78 | END 79 | $$ LANGUAGE plpgsql; 80 | SELECT plpgsql_nested(); 81 | plpgsql_nested 82 | ---------------- 83 | 84 | (1 row) 85 | 86 | SELECT COUNT(*) FROM pg_stat_kcache_detail WHERE top IS FALSE; 87 | count 88 | ------- 89 | 2 90 | (1 row) 91 | 92 | -------------------------------------------------------------------------------- /pg_stat_kcache--2.1.0--2.1.1.sql: -------------------------------------------------------------------------------- 1 | -- This program is open source, licensed under the PostgreSQL License. 2 | -- For license terms, see the LICENSE file. 3 | -- 4 | -- Copyright (c) 2014-2017, Dalibo 5 | -- Copyright (c) 2018-2024, The PoWA-team 6 | 7 | -- complain if script is sourced in psql, rather than via CREATE EXTENSION 8 | \echo Use "ALTER EXTENSION pg_stat_kcache" to load this file. \quit 9 | 10 | -------------------------------------------------------------------------------- /pg_stat_kcache--2.1.0.sql: -------------------------------------------------------------------------------- 1 | -- This program is open source, licensed under the PostgreSQL License. 2 | -- For license terms, see the LICENSE file. 3 | -- 4 | -- Copyright (c) 2014-2017, Dalibo 5 | -- Copyright (c) 2018-2024, The PoWA-team 6 | 7 | -- complain if script is sourced in psql, rather than via CREATE EXTENSION 8 | \echo Use "CREATE EXTENSION pg_stat_kcache" to load this file. \quit 9 | 10 | SET client_encoding = 'UTF8'; 11 | 12 | CREATE FUNCTION pg_stat_kcache( 13 | OUT queryid bigint, 14 | OUT userid oid, 15 | OUT dbid oid, 16 | OUT reads bigint, /* total reads, in bytes */ 17 | OUT writes bigint, /* total writes, in bytes */ 18 | OUT user_time double precision, /* total user CPU time used */ 19 | OUT system_time double precision, /* total system CPU time used */ 20 | OUT minflts bigint, /* total page reclaims (soft page faults) */ 21 | OUT majflts bigint, /* total page faults (hard page faults) */ 22 | OUT nswaps bigint, /* total swaps */ 23 | OUT msgsnds bigint, /* total IPC messages sent */ 24 | OUT msgrcvs bigint, /* total IPC messages received */ 25 | OUT nsignals bigint, /* total signals received */ 26 | OUT nvcsws bigint, /* total voluntary context switches */ 27 | OUT nivcsws bigint /* total involuntary context switches */ 28 | ) 29 | RETURNS SETOF record 30 | LANGUAGE c COST 1000 31 | AS '$libdir/pg_stat_kcache', 'pg_stat_kcache_2_1'; 32 | GRANT ALL ON FUNCTION pg_stat_kcache() TO public; 33 | 34 | CREATE FUNCTION pg_stat_kcache_reset() 35 | RETURNS void 36 | LANGUAGE c COST 1000 37 | AS '$libdir/pg_stat_kcache', 'pg_stat_kcache_reset'; 38 | REVOKE ALL ON FUNCTION pg_stat_kcache_reset() FROM public; 39 | 40 | CREATE VIEW pg_stat_kcache_detail AS 41 | SELECT s.query, d.datname, r.rolname, 42 | k.user_time, 43 | k.system_time, 44 | k.minflts, 45 | k.majflts, 46 | k.nswaps, 47 | k.reads AS reads, 48 | k.reads/(current_setting('block_size')::integer) AS reads_blks, 49 | k.writes AS writes, 50 | k.writes/(current_setting('block_size')::integer) AS writes_blks, 51 | k.msgsnds, 52 | k.msgrcvs, 53 | k.nsignals, 54 | k.nvcsws, 55 | k.nivcsws 56 | FROM pg_stat_kcache() k 57 | JOIN pg_stat_statements s 58 | ON k.queryid = s.queryid AND k.dbid = s.dbid AND k.userid = s.userid 59 | JOIN pg_database d 60 | ON d.oid = s.dbid 61 | JOIN pg_roles r 62 | ON r.oid = s.userid; 63 | GRANT SELECT ON pg_stat_kcache_detail TO public; 64 | 65 | CREATE VIEW pg_stat_kcache AS 66 | SELECT datname, 67 | SUM(user_time) AS user_time, 68 | SUM(system_time) AS system_time, 69 | SUM(minflts) AS minflts, 70 | SUM(majflts) AS majflts, 71 | SUM(nswaps) AS nswaps, 72 | SUM(reads) AS reads, 73 | SUM(reads_blks) AS reads_blks, 74 | SUM(writes) AS writes, 75 | SUM(writes_blks) AS writes_blks, 76 | SUM(msgsnds) AS msgsnds, 77 | SUM(msgrcvs) AS msgrcvs, 78 | SUM(nsignals) AS nsignals, 79 | SUM(nvcsws) AS nvcsws, 80 | SUM(nivcsws) AS nivcsws 81 | FROM pg_stat_kcache_detail 82 | GROUP BY datname; 83 | GRANT SELECT ON pg_stat_kcache TO public; 84 | -------------------------------------------------------------------------------- /pg_stat_kcache--2.1.1--2.1.2.sql: -------------------------------------------------------------------------------- 1 | -- This program is open source, licensed under the PostgreSQL License. 2 | -- For license terms, see the LICENSE file. 3 | -- 4 | -- Copyright (c) 2014-2017, Dalibo 5 | -- Copyright (c) 2018-2022, The PoWA-team 6 | 7 | -- complain if script is sourced in psql, rather than via CREATE EXTENSION 8 | \echo Use "ALTER EXTENSION pg_stat_kcache" to load this file. \quit 9 | 10 | -------------------------------------------------------------------------------- /pg_stat_kcache--2.1.1.sql: -------------------------------------------------------------------------------- 1 | -- This program is open source, licensed under the PostgreSQL License. 2 | -- For license terms, see the LICENSE file. 3 | -- 4 | -- Copyright (c) 2014-2017, Dalibo 5 | -- Copyright (c) 2018-2024, The PoWA-team 6 | 7 | -- complain if script is sourced in psql, rather than via CREATE EXTENSION 8 | \echo Use "CREATE EXTENSION pg_stat_kcache" to load this file. \quit 9 | 10 | SET client_encoding = 'UTF8'; 11 | 12 | CREATE FUNCTION pg_stat_kcache( 13 | OUT queryid bigint, 14 | OUT userid oid, 15 | OUT dbid oid, 16 | OUT reads bigint, /* total reads, in bytes */ 17 | OUT writes bigint, /* total writes, in bytes */ 18 | OUT user_time double precision, /* total user CPU time used */ 19 | OUT system_time double precision, /* total system CPU time used */ 20 | OUT minflts bigint, /* total page reclaims (soft page faults) */ 21 | OUT majflts bigint, /* total page faults (hard page faults) */ 22 | OUT nswaps bigint, /* total swaps */ 23 | OUT msgsnds bigint, /* total IPC messages sent */ 24 | OUT msgrcvs bigint, /* total IPC messages received */ 25 | OUT nsignals bigint, /* total signals received */ 26 | OUT nvcsws bigint, /* total voluntary context switches */ 27 | OUT nivcsws bigint /* total involuntary context switches */ 28 | ) 29 | RETURNS SETOF record 30 | LANGUAGE c COST 1000 31 | AS '$libdir/pg_stat_kcache', 'pg_stat_kcache_2_1'; 32 | GRANT ALL ON FUNCTION pg_stat_kcache() TO public; 33 | 34 | CREATE FUNCTION pg_stat_kcache_reset() 35 | RETURNS void 36 | LANGUAGE c COST 1000 37 | AS '$libdir/pg_stat_kcache', 'pg_stat_kcache_reset'; 38 | REVOKE ALL ON FUNCTION pg_stat_kcache_reset() FROM public; 39 | 40 | CREATE VIEW pg_stat_kcache_detail AS 41 | SELECT s.query, d.datname, r.rolname, 42 | k.user_time, 43 | k.system_time, 44 | k.minflts, 45 | k.majflts, 46 | k.nswaps, 47 | k.reads AS reads, 48 | k.reads/(current_setting('block_size')::integer) AS reads_blks, 49 | k.writes AS writes, 50 | k.writes/(current_setting('block_size')::integer) AS writes_blks, 51 | k.msgsnds, 52 | k.msgrcvs, 53 | k.nsignals, 54 | k.nvcsws, 55 | k.nivcsws 56 | FROM pg_stat_kcache() k 57 | JOIN pg_stat_statements s 58 | ON k.queryid = s.queryid AND k.dbid = s.dbid AND k.userid = s.userid 59 | JOIN pg_database d 60 | ON d.oid = s.dbid 61 | JOIN pg_roles r 62 | ON r.oid = s.userid; 63 | GRANT SELECT ON pg_stat_kcache_detail TO public; 64 | 65 | CREATE VIEW pg_stat_kcache AS 66 | SELECT datname, 67 | SUM(user_time) AS user_time, 68 | SUM(system_time) AS system_time, 69 | SUM(minflts) AS minflts, 70 | SUM(majflts) AS majflts, 71 | SUM(nswaps) AS nswaps, 72 | SUM(reads) AS reads, 73 | SUM(reads_blks) AS reads_blks, 74 | SUM(writes) AS writes, 75 | SUM(writes_blks) AS writes_blks, 76 | SUM(msgsnds) AS msgsnds, 77 | SUM(msgrcvs) AS msgrcvs, 78 | SUM(nsignals) AS nsignals, 79 | SUM(nvcsws) AS nvcsws, 80 | SUM(nivcsws) AS nivcsws 81 | FROM pg_stat_kcache_detail 82 | GROUP BY datname; 83 | GRANT SELECT ON pg_stat_kcache TO public; 84 | -------------------------------------------------------------------------------- /pg_stat_kcache--2.1.2--2.1.3.sql: -------------------------------------------------------------------------------- 1 | -- This program is open source, licensed under the PostgreSQL License. 2 | -- For license terms, see the LICENSE file. 3 | -- 4 | -- Copyright (c) 2014-2017, Dalibo 5 | -- Copyright (c) 2018-2024, The PoWA-team 6 | 7 | -- complain if script is sourced in psql, rather than via CREATE EXTENSION 8 | \echo Use "ALTER EXTENSION pg_stat_kcache" to load this file. \quit 9 | 10 | -------------------------------------------------------------------------------- /pg_stat_kcache--2.1.2.sql: -------------------------------------------------------------------------------- 1 | -- This program is open source, licensed under the PostgreSQL License. 2 | -- For license terms, see the LICENSE file. 3 | -- 4 | -- Copyright (c) 2014-2017, Dalibo 5 | -- Copyright (c) 2018-2024, The PoWA-team 6 | 7 | -- complain if script is sourced in psql, rather than via CREATE EXTENSION 8 | \echo Use "CREATE EXTENSION pg_stat_kcache" to load this file. \quit 9 | 10 | SET client_encoding = 'UTF8'; 11 | 12 | CREATE FUNCTION pg_stat_kcache( 13 | OUT queryid bigint, 14 | OUT userid oid, 15 | OUT dbid oid, 16 | OUT reads bigint, /* total reads, in bytes */ 17 | OUT writes bigint, /* total writes, in bytes */ 18 | OUT user_time double precision, /* total user CPU time used */ 19 | OUT system_time double precision, /* total system CPU time used */ 20 | OUT minflts bigint, /* total page reclaims (soft page faults) */ 21 | OUT majflts bigint, /* total page faults (hard page faults) */ 22 | OUT nswaps bigint, /* total swaps */ 23 | OUT msgsnds bigint, /* total IPC messages sent */ 24 | OUT msgrcvs bigint, /* total IPC messages received */ 25 | OUT nsignals bigint, /* total signals received */ 26 | OUT nvcsws bigint, /* total voluntary context switches */ 27 | OUT nivcsws bigint /* total involuntary context switches */ 28 | ) 29 | RETURNS SETOF record 30 | LANGUAGE c COST 1000 31 | AS '$libdir/pg_stat_kcache', 'pg_stat_kcache_2_1'; 32 | GRANT ALL ON FUNCTION pg_stat_kcache() TO public; 33 | 34 | CREATE FUNCTION pg_stat_kcache_reset() 35 | RETURNS void 36 | LANGUAGE c COST 1000 37 | AS '$libdir/pg_stat_kcache', 'pg_stat_kcache_reset'; 38 | REVOKE ALL ON FUNCTION pg_stat_kcache_reset() FROM public; 39 | 40 | CREATE VIEW pg_stat_kcache_detail AS 41 | SELECT s.query, d.datname, r.rolname, 42 | k.user_time, 43 | k.system_time, 44 | k.minflts, 45 | k.majflts, 46 | k.nswaps, 47 | k.reads AS reads, 48 | k.reads/(current_setting('block_size')::integer) AS reads_blks, 49 | k.writes AS writes, 50 | k.writes/(current_setting('block_size')::integer) AS writes_blks, 51 | k.msgsnds, 52 | k.msgrcvs, 53 | k.nsignals, 54 | k.nvcsws, 55 | k.nivcsws 56 | FROM pg_stat_kcache() k 57 | JOIN pg_stat_statements s 58 | ON k.queryid = s.queryid AND k.dbid = s.dbid AND k.userid = s.userid 59 | JOIN pg_database d 60 | ON d.oid = s.dbid 61 | JOIN pg_roles r 62 | ON r.oid = s.userid; 63 | GRANT SELECT ON pg_stat_kcache_detail TO public; 64 | 65 | CREATE VIEW pg_stat_kcache AS 66 | SELECT datname, 67 | SUM(user_time) AS user_time, 68 | SUM(system_time) AS system_time, 69 | SUM(minflts) AS minflts, 70 | SUM(majflts) AS majflts, 71 | SUM(nswaps) AS nswaps, 72 | SUM(reads) AS reads, 73 | SUM(reads_blks) AS reads_blks, 74 | SUM(writes) AS writes, 75 | SUM(writes_blks) AS writes_blks, 76 | SUM(msgsnds) AS msgsnds, 77 | SUM(msgrcvs) AS msgrcvs, 78 | SUM(nsignals) AS nsignals, 79 | SUM(nvcsws) AS nvcsws, 80 | SUM(nivcsws) AS nivcsws 81 | FROM pg_stat_kcache_detail 82 | GROUP BY datname; 83 | GRANT SELECT ON pg_stat_kcache TO public; 84 | -------------------------------------------------------------------------------- /pg_stat_kcache--2.1.3--2.2.0.sql: -------------------------------------------------------------------------------- 1 | -- This program is open source, licensed under the PostgreSQL License. 2 | -- For license terms, see the LICENSE file. 3 | -- 4 | -- Copyright (c) 2014-2017, Dalibo 5 | -- Copyright (c) 2018-2024, The PoWA-team 6 | 7 | -- complain if script is sourced in psql, rather than via CREATE EXTENSION 8 | \echo Use "ALTER EXTENSION pg_stat_kcache" to load this file. \quit 9 | 10 | DROP VIEW pg_stat_kcache; 11 | DROP VIEW pg_stat_kcache_detail; 12 | 13 | DROP FUNCTION pg_stat_kcache(); 14 | CREATE FUNCTION pg_stat_kcache( 15 | OUT queryid bigint, 16 | OUT top bool, 17 | OUT userid oid, 18 | OUT dbid oid, 19 | /* planning time */ 20 | OUT plan_reads bigint, /* total reads, in bytes */ 21 | OUT plan_writes bigint, /* total writes, in bytes */ 22 | OUT plan_user_time double precision, /* total user CPU time used */ 23 | OUT plan_system_time double precision, /* total system CPU time used */ 24 | OUT plan_minflts bigint, /* total page reclaims (soft page faults) */ 25 | OUT plan_majflts bigint, /* total page faults (hard page faults) */ 26 | OUT plan_nswaps bigint, /* total swaps */ 27 | OUT plan_msgsnds bigint, /* total IPC messages sent */ 28 | OUT plan_msgrcvs bigint, /* total IPC messages received */ 29 | OUT plan_nsignals bigint, /* total signals received */ 30 | OUT plan_nvcsws bigint, /* total voluntary context switches */ 31 | OUT plan_nivcsws bigint, /* total involuntary context switches */ 32 | /* execution time */ 33 | OUT exec_reads bigint, /* total reads, in bytes */ 34 | OUT exec_writes bigint, /* total writes, in bytes */ 35 | OUT exec_user_time double precision, /* total user CPU time used */ 36 | OUT exec_system_time double precision, /* total system CPU time used */ 37 | OUT exec_minflts bigint, /* total page reclaims (soft page faults) */ 38 | OUT exec_majflts bigint, /* total page faults (hard page faults) */ 39 | OUT exec_nswaps bigint, /* total swaps */ 40 | OUT exec_msgsnds bigint, /* total IPC messages sent */ 41 | OUT exec_msgrcvs bigint, /* total IPC messages received */ 42 | OUT exec_nsignals bigint, /* total signals received */ 43 | OUT exec_nvcsws bigint, /* total voluntary context switches */ 44 | OUT exec_nivcsws bigint /* total involuntary context switches */ 45 | ) 46 | RETURNS SETOF record 47 | LANGUAGE c COST 1000 48 | AS '$libdir/pg_stat_kcache', 'pg_stat_kcache_2_2'; 49 | GRANT ALL ON FUNCTION pg_stat_kcache() TO public; 50 | 51 | CREATE VIEW pg_stat_kcache_detail AS 52 | SELECT s.query, k.top, d.datname, r.rolname, 53 | k.plan_user_time, 54 | k.plan_system_time, 55 | k.plan_minflts, 56 | k.plan_majflts, 57 | k.plan_nswaps, 58 | k.plan_reads AS plan_reads, 59 | k.plan_reads/(current_setting('block_size')::integer) AS plan_reads_blks, 60 | k.plan_writes AS plan_writes, 61 | k.plan_writes/(current_setting('block_size')::integer) AS plan_writes_blks, 62 | k.plan_msgsnds, 63 | k.plan_msgrcvs, 64 | k.plan_nsignals, 65 | k.plan_nvcsws, 66 | k.plan_nivcsws, 67 | k.exec_user_time, 68 | k.exec_system_time, 69 | k.exec_minflts, 70 | k.exec_majflts, 71 | k.exec_nswaps, 72 | k.exec_reads AS exec_reads, 73 | k.exec_reads/(current_setting('block_size')::integer) AS exec_reads_blks, 74 | k.exec_writes AS exec_writes, 75 | k.exec_writes/(current_setting('block_size')::integer) AS exec_writes_blks, 76 | k.exec_msgsnds, 77 | k.exec_msgrcvs, 78 | k.exec_nsignals, 79 | k.exec_nvcsws, 80 | k.exec_nivcsws 81 | FROM pg_stat_kcache() k 82 | JOIN pg_stat_statements s 83 | ON k.queryid = s.queryid AND k.dbid = s.dbid AND k.userid = s.userid 84 | JOIN pg_database d 85 | ON d.oid = s.dbid 86 | JOIN pg_roles r 87 | ON r.oid = s.userid; 88 | GRANT SELECT ON pg_stat_kcache_detail TO public; 89 | 90 | CREATE VIEW pg_stat_kcache AS 91 | SELECT datname, 92 | SUM(plan_user_time) AS plan_user_time, 93 | SUM(plan_system_time) AS plan_system_time, 94 | SUM(plan_minflts) AS plan_minflts, 95 | SUM(plan_majflts) AS plan_majflts, 96 | SUM(plan_nswaps) AS plan_nswaps, 97 | SUM(plan_reads) AS plan_reads, 98 | SUM(plan_reads_blks) AS plan_reads_blks, 99 | SUM(plan_writes) AS plan_writes, 100 | SUM(plan_writes_blks) AS plan_writes_blks, 101 | SUM(plan_msgsnds) AS plan_msgsnds, 102 | SUM(plan_msgrcvs) AS plan_msgrcvs, 103 | SUM(plan_nsignals) AS plan_nsignals, 104 | SUM(plan_nvcsws) AS plan_nvcsws, 105 | SUM(plan_nivcsws) AS plan_nivcsws, 106 | SUM(exec_user_time) AS exec_user_time, 107 | SUM(exec_system_time) AS exec_system_time, 108 | SUM(exec_minflts) AS exec_minflts, 109 | SUM(exec_majflts) AS exec_majflts, 110 | SUM(exec_nswaps) AS exec_nswaps, 111 | SUM(exec_reads) AS exec_reads, 112 | SUM(exec_reads_blks) AS exec_reads_blks, 113 | SUM(exec_writes) AS exec_writes, 114 | SUM(exec_writes_blks) AS exec_writes_blks, 115 | SUM(exec_msgsnds) AS exec_msgsnds, 116 | SUM(exec_msgrcvs) AS exec_msgrcvs, 117 | SUM(exec_nsignals) AS exec_nsignals, 118 | SUM(exec_nvcsws) AS exec_nvcsws, 119 | SUM(exec_nivcsws) AS exec_nivcsws 120 | FROM pg_stat_kcache_detail 121 | WHERE top IS TRUE 122 | GROUP BY datname; 123 | GRANT SELECT ON pg_stat_kcache TO public; 124 | -------------------------------------------------------------------------------- /pg_stat_kcache--2.1.3.sql: -------------------------------------------------------------------------------- 1 | -- This program is open source, licensed under the PostgreSQL License. 2 | -- For license terms, see the LICENSE file. 3 | -- 4 | -- Copyright (c) 2014-2017, Dalibo 5 | -- Copyright (c) 2018-2024, The PoWA-team 6 | 7 | -- complain if script is sourced in psql, rather than via CREATE EXTENSION 8 | \echo Use "CREATE EXTENSION pg_stat_kcache" to load this file. \quit 9 | 10 | SET client_encoding = 'UTF8'; 11 | 12 | CREATE FUNCTION pg_stat_kcache( 13 | OUT queryid bigint, 14 | OUT userid oid, 15 | OUT dbid oid, 16 | OUT reads bigint, /* total reads, in bytes */ 17 | OUT writes bigint, /* total writes, in bytes */ 18 | OUT user_time double precision, /* total user CPU time used */ 19 | OUT system_time double precision, /* total system CPU time used */ 20 | OUT minflts bigint, /* total page reclaims (soft page faults) */ 21 | OUT majflts bigint, /* total page faults (hard page faults) */ 22 | OUT nswaps bigint, /* total swaps */ 23 | OUT msgsnds bigint, /* total IPC messages sent */ 24 | OUT msgrcvs bigint, /* total IPC messages received */ 25 | OUT nsignals bigint, /* total signals received */ 26 | OUT nvcsws bigint, /* total voluntary context switches */ 27 | OUT nivcsws bigint /* total involuntary context switches */ 28 | ) 29 | RETURNS SETOF record 30 | LANGUAGE c COST 1000 31 | AS '$libdir/pg_stat_kcache', 'pg_stat_kcache_2_1'; 32 | GRANT ALL ON FUNCTION pg_stat_kcache() TO public; 33 | 34 | CREATE FUNCTION pg_stat_kcache_reset() 35 | RETURNS void 36 | LANGUAGE c COST 1000 37 | AS '$libdir/pg_stat_kcache', 'pg_stat_kcache_reset'; 38 | REVOKE ALL ON FUNCTION pg_stat_kcache_reset() FROM public; 39 | 40 | CREATE VIEW pg_stat_kcache_detail AS 41 | SELECT s.query, d.datname, r.rolname, 42 | k.user_time, 43 | k.system_time, 44 | k.minflts, 45 | k.majflts, 46 | k.nswaps, 47 | k.reads AS reads, 48 | k.reads/(current_setting('block_size')::integer) AS reads_blks, 49 | k.writes AS writes, 50 | k.writes/(current_setting('block_size')::integer) AS writes_blks, 51 | k.msgsnds, 52 | k.msgrcvs, 53 | k.nsignals, 54 | k.nvcsws, 55 | k.nivcsws 56 | FROM pg_stat_kcache() k 57 | JOIN pg_stat_statements s 58 | ON k.queryid = s.queryid AND k.dbid = s.dbid AND k.userid = s.userid 59 | JOIN pg_database d 60 | ON d.oid = s.dbid 61 | JOIN pg_roles r 62 | ON r.oid = s.userid; 63 | GRANT SELECT ON pg_stat_kcache_detail TO public; 64 | 65 | CREATE VIEW pg_stat_kcache AS 66 | SELECT datname, 67 | SUM(user_time) AS user_time, 68 | SUM(system_time) AS system_time, 69 | SUM(minflts) AS minflts, 70 | SUM(majflts) AS majflts, 71 | SUM(nswaps) AS nswaps, 72 | SUM(reads) AS reads, 73 | SUM(reads_blks) AS reads_blks, 74 | SUM(writes) AS writes, 75 | SUM(writes_blks) AS writes_blks, 76 | SUM(msgsnds) AS msgsnds, 77 | SUM(msgrcvs) AS msgrcvs, 78 | SUM(nsignals) AS nsignals, 79 | SUM(nvcsws) AS nvcsws, 80 | SUM(nivcsws) AS nivcsws 81 | FROM pg_stat_kcache_detail 82 | GROUP BY datname; 83 | GRANT SELECT ON pg_stat_kcache TO public; 84 | -------------------------------------------------------------------------------- /pg_stat_kcache--2.2.0--2.2.1.sql: -------------------------------------------------------------------------------- 1 | -- This program is open source, licensed under the PostgreSQL License. 2 | -- For license terms, see the LICENSE file. 3 | -- 4 | -- Copyright (c) 2014-2017, Dalibo 5 | -- Copyright (c) 2018-2022, The PoWA-team 6 | 7 | -- complain if script is sourced in psql, rather than via CREATE EXTENSION 8 | \echo Use "ALTER EXTENSION pg_stat_kcache" to load this file. \quit 9 | 10 | -------------------------------------------------------------------------------- /pg_stat_kcache--2.2.0.sql: -------------------------------------------------------------------------------- 1 | -- This program is open source, licensed under the PostgreSQL License. 2 | -- For license terms, see the LICENSE file. 3 | -- 4 | -- Copyright (c) 2014-2017, Dalibo 5 | -- Copyright (c) 2018-2024, The PoWA-team 6 | 7 | -- complain if script is sourced in psql, rather than via CREATE EXTENSION 8 | \echo Use "CREATE EXTENSION pg_stat_kcache" to load this file. \quit 9 | 10 | SET client_encoding = 'UTF8'; 11 | 12 | CREATE FUNCTION pg_stat_kcache( 13 | OUT queryid bigint, 14 | OUT top bool, 15 | OUT userid oid, 16 | OUT dbid oid, 17 | /* planning time */ 18 | OUT plan_reads bigint, /* total reads, in bytes */ 19 | OUT plan_writes bigint, /* total writes, in bytes */ 20 | OUT plan_user_time double precision, /* total user CPU time used */ 21 | OUT plan_system_time double precision, /* total system CPU time used */ 22 | OUT plan_minflts bigint, /* total page reclaims (soft page faults) */ 23 | OUT plan_majflts bigint, /* total page faults (hard page faults) */ 24 | OUT plan_nswaps bigint, /* total swaps */ 25 | OUT plan_msgsnds bigint, /* total IPC messages sent */ 26 | OUT plan_msgrcvs bigint, /* total IPC messages received */ 27 | OUT plan_nsignals bigint, /* total signals received */ 28 | OUT plan_nvcsws bigint, /* total voluntary context switches */ 29 | OUT plan_nivcsws bigint, /* total involuntary context switches */ 30 | /* execution time */ 31 | OUT exec_reads bigint, /* total reads, in bytes */ 32 | OUT exec_writes bigint, /* total writes, in bytes */ 33 | OUT exec_user_time double precision, /* total user CPU time used */ 34 | OUT exec_system_time double precision, /* total system CPU time used */ 35 | OUT exec_minflts bigint, /* total page reclaims (soft page faults) */ 36 | OUT exec_majflts bigint, /* total page faults (hard page faults) */ 37 | OUT exec_nswaps bigint, /* total swaps */ 38 | OUT exec_msgsnds bigint, /* total IPC messages sent */ 39 | OUT exec_msgrcvs bigint, /* total IPC messages received */ 40 | OUT exec_nsignals bigint, /* total signals received */ 41 | OUT exec_nvcsws bigint, /* total voluntary context switches */ 42 | OUT exec_nivcsws bigint /* total involuntary context switches */ 43 | ) 44 | RETURNS SETOF record 45 | LANGUAGE c COST 1000 46 | AS '$libdir/pg_stat_kcache', 'pg_stat_kcache_2_2'; 47 | GRANT ALL ON FUNCTION pg_stat_kcache() TO public; 48 | 49 | CREATE FUNCTION pg_stat_kcache_reset() 50 | RETURNS void 51 | LANGUAGE c COST 1000 52 | AS '$libdir/pg_stat_kcache', 'pg_stat_kcache_reset'; 53 | REVOKE ALL ON FUNCTION pg_stat_kcache_reset() FROM public; 54 | 55 | CREATE VIEW pg_stat_kcache_detail AS 56 | SELECT s.query, k.top, d.datname, r.rolname, 57 | k.plan_user_time, 58 | k.plan_system_time, 59 | k.plan_minflts, 60 | k.plan_majflts, 61 | k.plan_nswaps, 62 | k.plan_reads AS plan_reads, 63 | k.plan_reads/(current_setting('block_size')::integer) AS plan_reads_blks, 64 | k.plan_writes AS plan_writes, 65 | k.plan_writes/(current_setting('block_size')::integer) AS plan_writes_blks, 66 | k.plan_msgsnds, 67 | k.plan_msgrcvs, 68 | k.plan_nsignals, 69 | k.plan_nvcsws, 70 | k.plan_nivcsws, 71 | k.exec_user_time, 72 | k.exec_system_time, 73 | k.exec_minflts, 74 | k.exec_majflts, 75 | k.exec_nswaps, 76 | k.exec_reads AS exec_reads, 77 | k.exec_reads/(current_setting('block_size')::integer) AS exec_reads_blks, 78 | k.exec_writes AS exec_writes, 79 | k.exec_writes/(current_setting('block_size')::integer) AS exec_writes_blks, 80 | k.exec_msgsnds, 81 | k.exec_msgrcvs, 82 | k.exec_nsignals, 83 | k.exec_nvcsws, 84 | k.exec_nivcsws 85 | FROM pg_stat_kcache() k 86 | JOIN pg_stat_statements s 87 | ON k.queryid = s.queryid AND k.dbid = s.dbid AND k.userid = s.userid 88 | JOIN pg_database d 89 | ON d.oid = s.dbid 90 | JOIN pg_roles r 91 | ON r.oid = s.userid; 92 | GRANT SELECT ON pg_stat_kcache_detail TO public; 93 | 94 | CREATE VIEW pg_stat_kcache AS 95 | SELECT datname, 96 | SUM(plan_user_time) AS plan_user_time, 97 | SUM(plan_system_time) AS plan_system_time, 98 | SUM(plan_minflts) AS plan_minflts, 99 | SUM(plan_majflts) AS plan_majflts, 100 | SUM(plan_nswaps) AS plan_nswaps, 101 | SUM(plan_reads) AS plan_reads, 102 | SUM(plan_reads_blks) AS plan_reads_blks, 103 | SUM(plan_writes) AS plan_writes, 104 | SUM(plan_writes_blks) AS plan_writes_blks, 105 | SUM(plan_msgsnds) AS plan_msgsnds, 106 | SUM(plan_msgrcvs) AS plan_msgrcvs, 107 | SUM(plan_nsignals) AS plan_nsignals, 108 | SUM(plan_nvcsws) AS plan_nvcsws, 109 | SUM(plan_nivcsws) AS plan_nivcsws, 110 | SUM(exec_user_time) AS exec_user_time, 111 | SUM(exec_system_time) AS exec_system_time, 112 | SUM(exec_minflts) AS exec_minflts, 113 | SUM(exec_majflts) AS exec_majflts, 114 | SUM(exec_nswaps) AS exec_nswaps, 115 | SUM(exec_reads) AS exec_reads, 116 | SUM(exec_reads_blks) AS exec_reads_blks, 117 | SUM(exec_writes) AS exec_writes, 118 | SUM(exec_writes_blks) AS exec_writes_blks, 119 | SUM(exec_msgsnds) AS exec_msgsnds, 120 | SUM(exec_msgrcvs) AS exec_msgrcvs, 121 | SUM(exec_nsignals) AS exec_nsignals, 122 | SUM(exec_nvcsws) AS exec_nvcsws, 123 | SUM(exec_nivcsws) AS exec_nivcsws 124 | FROM pg_stat_kcache_detail 125 | WHERE top IS TRUE 126 | GROUP BY datname; 127 | GRANT SELECT ON pg_stat_kcache TO public; 128 | -------------------------------------------------------------------------------- /pg_stat_kcache--2.2.1--2.2.2.sql: -------------------------------------------------------------------------------- 1 | -- This program is open source, licensed under the PostgreSQL License. 2 | -- For license terms, see the LICENSE file. 3 | -- 4 | -- Copyright (c) 2014-2017, Dalibo 5 | -- Copyright (c) 2018-2024, The PoWA-team 6 | 7 | -- complain if script is sourced in psql, rather than via CREATE EXTENSION 8 | \echo Use "ALTER EXTENSION pg_stat_kcache" to load this file. \quit 9 | 10 | -------------------------------------------------------------------------------- /pg_stat_kcache--2.2.1.sql: -------------------------------------------------------------------------------- 1 | -- This program is open source, licensed under the PostgreSQL License. 2 | -- For license terms, see the LICENSE file. 3 | -- 4 | -- Copyright (c) 2014-2017, Dalibo 5 | -- Copyright (c) 2018-2024, The PoWA-team 6 | 7 | -- complain if script is sourced in psql, rather than via CREATE EXTENSION 8 | \echo Use "CREATE EXTENSION pg_stat_kcache" to load this file. \quit 9 | 10 | SET client_encoding = 'UTF8'; 11 | 12 | CREATE FUNCTION pg_stat_kcache( 13 | OUT queryid bigint, 14 | OUT top bool, 15 | OUT userid oid, 16 | OUT dbid oid, 17 | /* planning time */ 18 | OUT plan_reads bigint, /* total reads, in bytes */ 19 | OUT plan_writes bigint, /* total writes, in bytes */ 20 | OUT plan_user_time double precision, /* total user CPU time used */ 21 | OUT plan_system_time double precision, /* total system CPU time used */ 22 | OUT plan_minflts bigint, /* total page reclaims (soft page faults) */ 23 | OUT plan_majflts bigint, /* total page faults (hard page faults) */ 24 | OUT plan_nswaps bigint, /* total swaps */ 25 | OUT plan_msgsnds bigint, /* total IPC messages sent */ 26 | OUT plan_msgrcvs bigint, /* total IPC messages received */ 27 | OUT plan_nsignals bigint, /* total signals received */ 28 | OUT plan_nvcsws bigint, /* total voluntary context switches */ 29 | OUT plan_nivcsws bigint, /* total involuntary context switches */ 30 | /* execution time */ 31 | OUT exec_reads bigint, /* total reads, in bytes */ 32 | OUT exec_writes bigint, /* total writes, in bytes */ 33 | OUT exec_user_time double precision, /* total user CPU time used */ 34 | OUT exec_system_time double precision, /* total system CPU time used */ 35 | OUT exec_minflts bigint, /* total page reclaims (soft page faults) */ 36 | OUT exec_majflts bigint, /* total page faults (hard page faults) */ 37 | OUT exec_nswaps bigint, /* total swaps */ 38 | OUT exec_msgsnds bigint, /* total IPC messages sent */ 39 | OUT exec_msgrcvs bigint, /* total IPC messages received */ 40 | OUT exec_nsignals bigint, /* total signals received */ 41 | OUT exec_nvcsws bigint, /* total voluntary context switches */ 42 | OUT exec_nivcsws bigint /* total involuntary context switches */ 43 | ) 44 | RETURNS SETOF record 45 | LANGUAGE c COST 1000 46 | AS '$libdir/pg_stat_kcache', 'pg_stat_kcache_2_2'; 47 | GRANT ALL ON FUNCTION pg_stat_kcache() TO public; 48 | 49 | CREATE FUNCTION pg_stat_kcache_reset() 50 | RETURNS void 51 | LANGUAGE c COST 1000 52 | AS '$libdir/pg_stat_kcache', 'pg_stat_kcache_reset'; 53 | REVOKE ALL ON FUNCTION pg_stat_kcache_reset() FROM public; 54 | 55 | CREATE VIEW pg_stat_kcache_detail AS 56 | SELECT s.query, k.top, d.datname, r.rolname, 57 | k.plan_user_time, 58 | k.plan_system_time, 59 | k.plan_minflts, 60 | k.plan_majflts, 61 | k.plan_nswaps, 62 | k.plan_reads AS plan_reads, 63 | k.plan_reads/(current_setting('block_size')::integer) AS plan_reads_blks, 64 | k.plan_writes AS plan_writes, 65 | k.plan_writes/(current_setting('block_size')::integer) AS plan_writes_blks, 66 | k.plan_msgsnds, 67 | k.plan_msgrcvs, 68 | k.plan_nsignals, 69 | k.plan_nvcsws, 70 | k.plan_nivcsws, 71 | k.exec_user_time, 72 | k.exec_system_time, 73 | k.exec_minflts, 74 | k.exec_majflts, 75 | k.exec_nswaps, 76 | k.exec_reads AS exec_reads, 77 | k.exec_reads/(current_setting('block_size')::integer) AS exec_reads_blks, 78 | k.exec_writes AS exec_writes, 79 | k.exec_writes/(current_setting('block_size')::integer) AS exec_writes_blks, 80 | k.exec_msgsnds, 81 | k.exec_msgrcvs, 82 | k.exec_nsignals, 83 | k.exec_nvcsws, 84 | k.exec_nivcsws 85 | FROM pg_stat_kcache() k 86 | JOIN pg_stat_statements s 87 | ON k.queryid = s.queryid AND k.dbid = s.dbid AND k.userid = s.userid 88 | JOIN pg_database d 89 | ON d.oid = s.dbid 90 | JOIN pg_roles r 91 | ON r.oid = s.userid; 92 | GRANT SELECT ON pg_stat_kcache_detail TO public; 93 | 94 | CREATE VIEW pg_stat_kcache AS 95 | SELECT datname, 96 | SUM(plan_user_time) AS plan_user_time, 97 | SUM(plan_system_time) AS plan_system_time, 98 | SUM(plan_minflts) AS plan_minflts, 99 | SUM(plan_majflts) AS plan_majflts, 100 | SUM(plan_nswaps) AS plan_nswaps, 101 | SUM(plan_reads) AS plan_reads, 102 | SUM(plan_reads_blks) AS plan_reads_blks, 103 | SUM(plan_writes) AS plan_writes, 104 | SUM(plan_writes_blks) AS plan_writes_blks, 105 | SUM(plan_msgsnds) AS plan_msgsnds, 106 | SUM(plan_msgrcvs) AS plan_msgrcvs, 107 | SUM(plan_nsignals) AS plan_nsignals, 108 | SUM(plan_nvcsws) AS plan_nvcsws, 109 | SUM(plan_nivcsws) AS plan_nivcsws, 110 | SUM(exec_user_time) AS exec_user_time, 111 | SUM(exec_system_time) AS exec_system_time, 112 | SUM(exec_minflts) AS exec_minflts, 113 | SUM(exec_majflts) AS exec_majflts, 114 | SUM(exec_nswaps) AS exec_nswaps, 115 | SUM(exec_reads) AS exec_reads, 116 | SUM(exec_reads_blks) AS exec_reads_blks, 117 | SUM(exec_writes) AS exec_writes, 118 | SUM(exec_writes_blks) AS exec_writes_blks, 119 | SUM(exec_msgsnds) AS exec_msgsnds, 120 | SUM(exec_msgrcvs) AS exec_msgrcvs, 121 | SUM(exec_nsignals) AS exec_nsignals, 122 | SUM(exec_nvcsws) AS exec_nvcsws, 123 | SUM(exec_nivcsws) AS exec_nivcsws 124 | FROM pg_stat_kcache_detail 125 | WHERE top IS TRUE 126 | GROUP BY datname; 127 | GRANT SELECT ON pg_stat_kcache TO public; 128 | -------------------------------------------------------------------------------- /pg_stat_kcache--2.2.2--2.2.3.sql: -------------------------------------------------------------------------------- 1 | -- This program is open source, licensed under the PostgreSQL License. 2 | -- For license terms, see the LICENSE file. 3 | -- 4 | -- Copyright (c) 2014-2017, Dalibo 5 | -- Copyright (c) 2018-2024, The PoWA-team 6 | 7 | -- complain if script is sourced in psql, rather than via CREATE EXTENSION 8 | \echo Use "ALTER EXTENSION pg_stat_kcache" to load this file. \quit 9 | 10 | -------------------------------------------------------------------------------- /pg_stat_kcache--2.2.2.sql: -------------------------------------------------------------------------------- 1 | -- This program is open source, licensed under the PostgreSQL License. 2 | -- For license terms, see the LICENSE file. 3 | -- 4 | -- Copyright (c) 2014-2017, Dalibo 5 | -- Copyright (c) 2018-2024, The PoWA-team 6 | 7 | -- complain if script is sourced in psql, rather than via CREATE EXTENSION 8 | \echo Use "CREATE EXTENSION pg_stat_kcache" to load this file. \quit 9 | 10 | SET client_encoding = 'UTF8'; 11 | 12 | CREATE FUNCTION pg_stat_kcache( 13 | OUT queryid bigint, 14 | OUT top bool, 15 | OUT userid oid, 16 | OUT dbid oid, 17 | /* planning time */ 18 | OUT plan_reads bigint, /* total reads, in bytes */ 19 | OUT plan_writes bigint, /* total writes, in bytes */ 20 | OUT plan_user_time double precision, /* total user CPU time used */ 21 | OUT plan_system_time double precision, /* total system CPU time used */ 22 | OUT plan_minflts bigint, /* total page reclaims (soft page faults) */ 23 | OUT plan_majflts bigint, /* total page faults (hard page faults) */ 24 | OUT plan_nswaps bigint, /* total swaps */ 25 | OUT plan_msgsnds bigint, /* total IPC messages sent */ 26 | OUT plan_msgrcvs bigint, /* total IPC messages received */ 27 | OUT plan_nsignals bigint, /* total signals received */ 28 | OUT plan_nvcsws bigint, /* total voluntary context switches */ 29 | OUT plan_nivcsws bigint, /* total involuntary context switches */ 30 | /* execution time */ 31 | OUT exec_reads bigint, /* total reads, in bytes */ 32 | OUT exec_writes bigint, /* total writes, in bytes */ 33 | OUT exec_user_time double precision, /* total user CPU time used */ 34 | OUT exec_system_time double precision, /* total system CPU time used */ 35 | OUT exec_minflts bigint, /* total page reclaims (soft page faults) */ 36 | OUT exec_majflts bigint, /* total page faults (hard page faults) */ 37 | OUT exec_nswaps bigint, /* total swaps */ 38 | OUT exec_msgsnds bigint, /* total IPC messages sent */ 39 | OUT exec_msgrcvs bigint, /* total IPC messages received */ 40 | OUT exec_nsignals bigint, /* total signals received */ 41 | OUT exec_nvcsws bigint, /* total voluntary context switches */ 42 | OUT exec_nivcsws bigint /* total involuntary context switches */ 43 | ) 44 | RETURNS SETOF record 45 | LANGUAGE c COST 1000 46 | AS '$libdir/pg_stat_kcache', 'pg_stat_kcache_2_2'; 47 | GRANT ALL ON FUNCTION pg_stat_kcache() TO public; 48 | 49 | CREATE FUNCTION pg_stat_kcache_reset() 50 | RETURNS void 51 | LANGUAGE c COST 1000 52 | AS '$libdir/pg_stat_kcache', 'pg_stat_kcache_reset'; 53 | REVOKE ALL ON FUNCTION pg_stat_kcache_reset() FROM public; 54 | 55 | CREATE VIEW pg_stat_kcache_detail AS 56 | SELECT s.query, k.top, d.datname, r.rolname, 57 | k.plan_user_time, 58 | k.plan_system_time, 59 | k.plan_minflts, 60 | k.plan_majflts, 61 | k.plan_nswaps, 62 | k.plan_reads AS plan_reads, 63 | k.plan_reads/(current_setting('block_size')::integer) AS plan_reads_blks, 64 | k.plan_writes AS plan_writes, 65 | k.plan_writes/(current_setting('block_size')::integer) AS plan_writes_blks, 66 | k.plan_msgsnds, 67 | k.plan_msgrcvs, 68 | k.plan_nsignals, 69 | k.plan_nvcsws, 70 | k.plan_nivcsws, 71 | k.exec_user_time, 72 | k.exec_system_time, 73 | k.exec_minflts, 74 | k.exec_majflts, 75 | k.exec_nswaps, 76 | k.exec_reads AS exec_reads, 77 | k.exec_reads/(current_setting('block_size')::integer) AS exec_reads_blks, 78 | k.exec_writes AS exec_writes, 79 | k.exec_writes/(current_setting('block_size')::integer) AS exec_writes_blks, 80 | k.exec_msgsnds, 81 | k.exec_msgrcvs, 82 | k.exec_nsignals, 83 | k.exec_nvcsws, 84 | k.exec_nivcsws 85 | FROM pg_stat_kcache() k 86 | JOIN pg_stat_statements s 87 | ON k.queryid = s.queryid AND k.dbid = s.dbid AND k.userid = s.userid 88 | JOIN pg_database d 89 | ON d.oid = s.dbid 90 | JOIN pg_roles r 91 | ON r.oid = s.userid; 92 | GRANT SELECT ON pg_stat_kcache_detail TO public; 93 | 94 | CREATE VIEW pg_stat_kcache AS 95 | SELECT datname, 96 | SUM(plan_user_time) AS plan_user_time, 97 | SUM(plan_system_time) AS plan_system_time, 98 | SUM(plan_minflts) AS plan_minflts, 99 | SUM(plan_majflts) AS plan_majflts, 100 | SUM(plan_nswaps) AS plan_nswaps, 101 | SUM(plan_reads) AS plan_reads, 102 | SUM(plan_reads_blks) AS plan_reads_blks, 103 | SUM(plan_writes) AS plan_writes, 104 | SUM(plan_writes_blks) AS plan_writes_blks, 105 | SUM(plan_msgsnds) AS plan_msgsnds, 106 | SUM(plan_msgrcvs) AS plan_msgrcvs, 107 | SUM(plan_nsignals) AS plan_nsignals, 108 | SUM(plan_nvcsws) AS plan_nvcsws, 109 | SUM(plan_nivcsws) AS plan_nivcsws, 110 | SUM(exec_user_time) AS exec_user_time, 111 | SUM(exec_system_time) AS exec_system_time, 112 | SUM(exec_minflts) AS exec_minflts, 113 | SUM(exec_majflts) AS exec_majflts, 114 | SUM(exec_nswaps) AS exec_nswaps, 115 | SUM(exec_reads) AS exec_reads, 116 | SUM(exec_reads_blks) AS exec_reads_blks, 117 | SUM(exec_writes) AS exec_writes, 118 | SUM(exec_writes_blks) AS exec_writes_blks, 119 | SUM(exec_msgsnds) AS exec_msgsnds, 120 | SUM(exec_msgrcvs) AS exec_msgrcvs, 121 | SUM(exec_nsignals) AS exec_nsignals, 122 | SUM(exec_nvcsws) AS exec_nvcsws, 123 | SUM(exec_nivcsws) AS exec_nivcsws 124 | FROM pg_stat_kcache_detail 125 | WHERE top IS TRUE 126 | GROUP BY datname; 127 | GRANT SELECT ON pg_stat_kcache TO public; 128 | -------------------------------------------------------------------------------- /pg_stat_kcache--2.2.3--2.3.0.sql: -------------------------------------------------------------------------------- 1 | -- This program is open source, licensed under the PostgreSQL License. 2 | -- For license terms, see the LICENSE file. 3 | -- 4 | -- Copyright (c) 2014-2017, Dalibo 5 | -- Copyright (c) 2018-2024, The PoWA-team 6 | 7 | -- complain if script is sourced in psql, rather than via CREATE EXTENSION 8 | \echo Use "ALTER EXTENSION pg_stat_kcache" to load this file. \quit 9 | 10 | DROP VIEW pg_stat_kcache_detail; 11 | DROP VIEW pg_stat_kcache; 12 | DROP FUNCTION pg_stat_kcache(); 13 | 14 | CREATE FUNCTION pg_stat_kcache( 15 | OUT queryid bigint, 16 | OUT top bool, 17 | OUT userid oid, 18 | OUT dbid oid, 19 | /* planning time */ 20 | OUT plan_reads bigint, /* total reads, in bytes */ 21 | OUT plan_writes bigint, /* total writes, in bytes */ 22 | OUT plan_user_time double precision, /* total user CPU time used */ 23 | OUT plan_system_time double precision, /* total system CPU time used */ 24 | OUT plan_minflts bigint, /* total page reclaims (soft page faults) */ 25 | OUT plan_majflts bigint, /* total page faults (hard page faults) */ 26 | OUT plan_nswaps bigint, /* total swaps */ 27 | OUT plan_msgsnds bigint, /* total IPC messages sent */ 28 | OUT plan_msgrcvs bigint, /* total IPC messages received */ 29 | OUT plan_nsignals bigint, /* total signals received */ 30 | OUT plan_nvcsws bigint, /* total voluntary context switches */ 31 | OUT plan_nivcsws bigint, /* total involuntary context switches */ 32 | /* execution time */ 33 | OUT exec_reads bigint, /* total reads, in bytes */ 34 | OUT exec_writes bigint, /* total writes, in bytes */ 35 | OUT exec_user_time double precision, /* total user CPU time used */ 36 | OUT exec_system_time double precision, /* total system CPU time used */ 37 | OUT exec_minflts bigint, /* total page reclaims (soft page faults) */ 38 | OUT exec_majflts bigint, /* total page faults (hard page faults) */ 39 | OUT exec_nswaps bigint, /* total swaps */ 40 | OUT exec_msgsnds bigint, /* total IPC messages sent */ 41 | OUT exec_msgrcvs bigint, /* total IPC messages received */ 42 | OUT exec_nsignals bigint, /* total signals received */ 43 | OUT exec_nvcsws bigint, /* total voluntary context switches */ 44 | OUT exec_nivcsws bigint, /* total involuntary context switches */ 45 | /* metadata */ 46 | OUT stats_since timestamptz /* entry creation time */ 47 | ) 48 | RETURNS SETOF record 49 | LANGUAGE c COST 1000 50 | AS '$libdir/pg_stat_kcache', 'pg_stat_kcache_2_3'; 51 | GRANT ALL ON FUNCTION pg_stat_kcache() TO public; 52 | 53 | CREATE VIEW pg_stat_kcache_detail AS 54 | SELECT s.query, k.top, d.datname, r.rolname, 55 | k.plan_user_time, 56 | k.plan_system_time, 57 | k.plan_minflts, 58 | k.plan_majflts, 59 | k.plan_nswaps, 60 | k.plan_reads AS plan_reads, 61 | k.plan_reads/(current_setting('block_size')::integer) AS plan_reads_blks, 62 | k.plan_writes AS plan_writes, 63 | k.plan_writes/(current_setting('block_size')::integer) AS plan_writes_blks, 64 | k.plan_msgsnds, 65 | k.plan_msgrcvs, 66 | k.plan_nsignals, 67 | k.plan_nvcsws, 68 | k.plan_nivcsws, 69 | k.exec_user_time, 70 | k.exec_system_time, 71 | k.exec_minflts, 72 | k.exec_majflts, 73 | k.exec_nswaps, 74 | k.exec_reads AS exec_reads, 75 | k.exec_reads/(current_setting('block_size')::integer) AS exec_reads_blks, 76 | k.exec_writes AS exec_writes, 77 | k.exec_writes/(current_setting('block_size')::integer) AS exec_writes_blks, 78 | k.exec_msgsnds, 79 | k.exec_msgrcvs, 80 | k.exec_nsignals, 81 | k.exec_nvcsws, 82 | k.exec_nivcsws, 83 | k.stats_since 84 | FROM pg_stat_kcache() k 85 | JOIN pg_stat_statements s 86 | ON k.queryid = s.queryid AND k.dbid = s.dbid AND k.userid = s.userid 87 | JOIN pg_database d 88 | ON d.oid = s.dbid 89 | JOIN pg_roles r 90 | ON r.oid = s.userid; 91 | GRANT SELECT ON pg_stat_kcache_detail TO public; 92 | 93 | CREATE VIEW pg_stat_kcache AS 94 | SELECT datname, 95 | SUM(plan_user_time) AS plan_user_time, 96 | SUM(plan_system_time) AS plan_system_time, 97 | SUM(plan_minflts) AS plan_minflts, 98 | SUM(plan_majflts) AS plan_majflts, 99 | SUM(plan_nswaps) AS plan_nswaps, 100 | SUM(plan_reads) AS plan_reads, 101 | SUM(plan_reads_blks) AS plan_reads_blks, 102 | SUM(plan_writes) AS plan_writes, 103 | SUM(plan_writes_blks) AS plan_writes_blks, 104 | SUM(plan_msgsnds) AS plan_msgsnds, 105 | SUM(plan_msgrcvs) AS plan_msgrcvs, 106 | SUM(plan_nsignals) AS plan_nsignals, 107 | SUM(plan_nvcsws) AS plan_nvcsws, 108 | SUM(plan_nivcsws) AS plan_nivcsws, 109 | SUM(exec_user_time) AS exec_user_time, 110 | SUM(exec_system_time) AS exec_system_time, 111 | SUM(exec_minflts) AS exec_minflts, 112 | SUM(exec_majflts) AS exec_majflts, 113 | SUM(exec_nswaps) AS exec_nswaps, 114 | SUM(exec_reads) AS exec_reads, 115 | SUM(exec_reads_blks) AS exec_reads_blks, 116 | SUM(exec_writes) AS exec_writes, 117 | SUM(exec_writes_blks) AS exec_writes_blks, 118 | SUM(exec_msgsnds) AS exec_msgsnds, 119 | SUM(exec_msgrcvs) AS exec_msgrcvs, 120 | SUM(exec_nsignals) AS exec_nsignals, 121 | SUM(exec_nvcsws) AS exec_nvcsws, 122 | SUM(exec_nivcsws) AS exec_nivcsws, 123 | MIN(stats_since) AS stats_since 124 | FROM pg_stat_kcache_detail 125 | WHERE top IS TRUE 126 | GROUP BY datname; 127 | GRANT SELECT ON pg_stat_kcache TO public; 128 | -------------------------------------------------------------------------------- /pg_stat_kcache--2.2.3.sql: -------------------------------------------------------------------------------- 1 | -- This program is open source, licensed under the PostgreSQL License. 2 | -- For license terms, see the LICENSE file. 3 | -- 4 | -- Copyright (c) 2014-2017, Dalibo 5 | -- Copyright (c) 2018-2024, The PoWA-team 6 | 7 | -- complain if script is sourced in psql, rather than via CREATE EXTENSION 8 | \echo Use "CREATE EXTENSION pg_stat_kcache" to load this file. \quit 9 | 10 | SET client_encoding = 'UTF8'; 11 | 12 | CREATE FUNCTION pg_stat_kcache( 13 | OUT queryid bigint, 14 | OUT top bool, 15 | OUT userid oid, 16 | OUT dbid oid, 17 | /* planning time */ 18 | OUT plan_reads bigint, /* total reads, in bytes */ 19 | OUT plan_writes bigint, /* total writes, in bytes */ 20 | OUT plan_user_time double precision, /* total user CPU time used */ 21 | OUT plan_system_time double precision, /* total system CPU time used */ 22 | OUT plan_minflts bigint, /* total page reclaims (soft page faults) */ 23 | OUT plan_majflts bigint, /* total page faults (hard page faults) */ 24 | OUT plan_nswaps bigint, /* total swaps */ 25 | OUT plan_msgsnds bigint, /* total IPC messages sent */ 26 | OUT plan_msgrcvs bigint, /* total IPC messages received */ 27 | OUT plan_nsignals bigint, /* total signals received */ 28 | OUT plan_nvcsws bigint, /* total voluntary context switches */ 29 | OUT plan_nivcsws bigint, /* total involuntary context switches */ 30 | /* execution time */ 31 | OUT exec_reads bigint, /* total reads, in bytes */ 32 | OUT exec_writes bigint, /* total writes, in bytes */ 33 | OUT exec_user_time double precision, /* total user CPU time used */ 34 | OUT exec_system_time double precision, /* total system CPU time used */ 35 | OUT exec_minflts bigint, /* total page reclaims (soft page faults) */ 36 | OUT exec_majflts bigint, /* total page faults (hard page faults) */ 37 | OUT exec_nswaps bigint, /* total swaps */ 38 | OUT exec_msgsnds bigint, /* total IPC messages sent */ 39 | OUT exec_msgrcvs bigint, /* total IPC messages received */ 40 | OUT exec_nsignals bigint, /* total signals received */ 41 | OUT exec_nvcsws bigint, /* total voluntary context switches */ 42 | OUT exec_nivcsws bigint /* total involuntary context switches */ 43 | ) 44 | RETURNS SETOF record 45 | LANGUAGE c COST 1000 46 | AS '$libdir/pg_stat_kcache', 'pg_stat_kcache_2_2'; 47 | GRANT ALL ON FUNCTION pg_stat_kcache() TO public; 48 | 49 | CREATE FUNCTION pg_stat_kcache_reset() 50 | RETURNS void 51 | LANGUAGE c COST 1000 52 | AS '$libdir/pg_stat_kcache', 'pg_stat_kcache_reset'; 53 | REVOKE ALL ON FUNCTION pg_stat_kcache_reset() FROM public; 54 | 55 | CREATE VIEW pg_stat_kcache_detail AS 56 | SELECT s.query, k.top, d.datname, r.rolname, 57 | k.plan_user_time, 58 | k.plan_system_time, 59 | k.plan_minflts, 60 | k.plan_majflts, 61 | k.plan_nswaps, 62 | k.plan_reads AS plan_reads, 63 | k.plan_reads/(current_setting('block_size')::integer) AS plan_reads_blks, 64 | k.plan_writes AS plan_writes, 65 | k.plan_writes/(current_setting('block_size')::integer) AS plan_writes_blks, 66 | k.plan_msgsnds, 67 | k.plan_msgrcvs, 68 | k.plan_nsignals, 69 | k.plan_nvcsws, 70 | k.plan_nivcsws, 71 | k.exec_user_time, 72 | k.exec_system_time, 73 | k.exec_minflts, 74 | k.exec_majflts, 75 | k.exec_nswaps, 76 | k.exec_reads AS exec_reads, 77 | k.exec_reads/(current_setting('block_size')::integer) AS exec_reads_blks, 78 | k.exec_writes AS exec_writes, 79 | k.exec_writes/(current_setting('block_size')::integer) AS exec_writes_blks, 80 | k.exec_msgsnds, 81 | k.exec_msgrcvs, 82 | k.exec_nsignals, 83 | k.exec_nvcsws, 84 | k.exec_nivcsws 85 | FROM pg_stat_kcache() k 86 | JOIN pg_stat_statements s 87 | ON k.queryid = s.queryid AND k.dbid = s.dbid AND k.userid = s.userid 88 | JOIN pg_database d 89 | ON d.oid = s.dbid 90 | JOIN pg_roles r 91 | ON r.oid = s.userid; 92 | GRANT SELECT ON pg_stat_kcache_detail TO public; 93 | 94 | CREATE VIEW pg_stat_kcache AS 95 | SELECT datname, 96 | SUM(plan_user_time) AS plan_user_time, 97 | SUM(plan_system_time) AS plan_system_time, 98 | SUM(plan_minflts) AS plan_minflts, 99 | SUM(plan_majflts) AS plan_majflts, 100 | SUM(plan_nswaps) AS plan_nswaps, 101 | SUM(plan_reads) AS plan_reads, 102 | SUM(plan_reads_blks) AS plan_reads_blks, 103 | SUM(plan_writes) AS plan_writes, 104 | SUM(plan_writes_blks) AS plan_writes_blks, 105 | SUM(plan_msgsnds) AS plan_msgsnds, 106 | SUM(plan_msgrcvs) AS plan_msgrcvs, 107 | SUM(plan_nsignals) AS plan_nsignals, 108 | SUM(plan_nvcsws) AS plan_nvcsws, 109 | SUM(plan_nivcsws) AS plan_nivcsws, 110 | SUM(exec_user_time) AS exec_user_time, 111 | SUM(exec_system_time) AS exec_system_time, 112 | SUM(exec_minflts) AS exec_minflts, 113 | SUM(exec_majflts) AS exec_majflts, 114 | SUM(exec_nswaps) AS exec_nswaps, 115 | SUM(exec_reads) AS exec_reads, 116 | SUM(exec_reads_blks) AS exec_reads_blks, 117 | SUM(exec_writes) AS exec_writes, 118 | SUM(exec_writes_blks) AS exec_writes_blks, 119 | SUM(exec_msgsnds) AS exec_msgsnds, 120 | SUM(exec_msgrcvs) AS exec_msgrcvs, 121 | SUM(exec_nsignals) AS exec_nsignals, 122 | SUM(exec_nvcsws) AS exec_nvcsws, 123 | SUM(exec_nivcsws) AS exec_nivcsws 124 | FROM pg_stat_kcache_detail 125 | WHERE top IS TRUE 126 | GROUP BY datname; 127 | GRANT SELECT ON pg_stat_kcache TO public; 128 | -------------------------------------------------------------------------------- /pg_stat_kcache--2.3.0.sql: -------------------------------------------------------------------------------- 1 | -- This program is open source, licensed under the PostgreSQL License. 2 | -- For license terms, see the LICENSE file. 3 | -- 4 | -- Copyright (c) 2014-2017, Dalibo 5 | -- Copyright (c) 2018-2024, The PoWA-team 6 | 7 | -- complain if script is sourced in psql, rather than via CREATE EXTENSION 8 | \echo Use "CREATE EXTENSION pg_stat_kcache" to load this file. \quit 9 | 10 | SET client_encoding = 'UTF8'; 11 | 12 | CREATE FUNCTION pg_stat_kcache( 13 | OUT queryid bigint, 14 | OUT top bool, 15 | OUT userid oid, 16 | OUT dbid oid, 17 | /* planning time */ 18 | OUT plan_reads bigint, /* total reads, in bytes */ 19 | OUT plan_writes bigint, /* total writes, in bytes */ 20 | OUT plan_user_time double precision, /* total user CPU time used */ 21 | OUT plan_system_time double precision, /* total system CPU time used */ 22 | OUT plan_minflts bigint, /* total page reclaims (soft page faults) */ 23 | OUT plan_majflts bigint, /* total page faults (hard page faults) */ 24 | OUT plan_nswaps bigint, /* total swaps */ 25 | OUT plan_msgsnds bigint, /* total IPC messages sent */ 26 | OUT plan_msgrcvs bigint, /* total IPC messages received */ 27 | OUT plan_nsignals bigint, /* total signals received */ 28 | OUT plan_nvcsws bigint, /* total voluntary context switches */ 29 | OUT plan_nivcsws bigint, /* total involuntary context switches */ 30 | /* execution time */ 31 | OUT exec_reads bigint, /* total reads, in bytes */ 32 | OUT exec_writes bigint, /* total writes, in bytes */ 33 | OUT exec_user_time double precision, /* total user CPU time used */ 34 | OUT exec_system_time double precision, /* total system CPU time used */ 35 | OUT exec_minflts bigint, /* total page reclaims (soft page faults) */ 36 | OUT exec_majflts bigint, /* total page faults (hard page faults) */ 37 | OUT exec_nswaps bigint, /* total swaps */ 38 | OUT exec_msgsnds bigint, /* total IPC messages sent */ 39 | OUT exec_msgrcvs bigint, /* total IPC messages received */ 40 | OUT exec_nsignals bigint, /* total signals received */ 41 | OUT exec_nvcsws bigint, /* total voluntary context switches */ 42 | OUT exec_nivcsws bigint, /* total involuntary context switches */ 43 | /* metadata */ 44 | OUT stats_since timestamptz /* entry creation time */ 45 | ) 46 | RETURNS SETOF record 47 | LANGUAGE c COST 1000 48 | AS '$libdir/pg_stat_kcache', 'pg_stat_kcache_2_3'; 49 | GRANT ALL ON FUNCTION pg_stat_kcache() TO public; 50 | 51 | CREATE FUNCTION pg_stat_kcache_reset() 52 | RETURNS void 53 | LANGUAGE c COST 1000 54 | AS '$libdir/pg_stat_kcache', 'pg_stat_kcache_reset'; 55 | REVOKE ALL ON FUNCTION pg_stat_kcache_reset() FROM public; 56 | 57 | CREATE VIEW pg_stat_kcache_detail AS 58 | SELECT s.query, k.top, d.datname, r.rolname, 59 | k.plan_user_time, 60 | k.plan_system_time, 61 | k.plan_minflts, 62 | k.plan_majflts, 63 | k.plan_nswaps, 64 | k.plan_reads AS plan_reads, 65 | k.plan_reads/(current_setting('block_size')::integer) AS plan_reads_blks, 66 | k.plan_writes AS plan_writes, 67 | k.plan_writes/(current_setting('block_size')::integer) AS plan_writes_blks, 68 | k.plan_msgsnds, 69 | k.plan_msgrcvs, 70 | k.plan_nsignals, 71 | k.plan_nvcsws, 72 | k.plan_nivcsws, 73 | k.exec_user_time, 74 | k.exec_system_time, 75 | k.exec_minflts, 76 | k.exec_majflts, 77 | k.exec_nswaps, 78 | k.exec_reads AS exec_reads, 79 | k.exec_reads/(current_setting('block_size')::integer) AS exec_reads_blks, 80 | k.exec_writes AS exec_writes, 81 | k.exec_writes/(current_setting('block_size')::integer) AS exec_writes_blks, 82 | k.exec_msgsnds, 83 | k.exec_msgrcvs, 84 | k.exec_nsignals, 85 | k.exec_nvcsws, 86 | k.exec_nivcsws, 87 | k.stats_since 88 | FROM pg_stat_kcache() k 89 | JOIN pg_stat_statements s 90 | ON k.queryid = s.queryid AND k.dbid = s.dbid AND k.userid = s.userid 91 | JOIN pg_database d 92 | ON d.oid = s.dbid 93 | JOIN pg_roles r 94 | ON r.oid = s.userid; 95 | GRANT SELECT ON pg_stat_kcache_detail TO public; 96 | 97 | CREATE VIEW pg_stat_kcache AS 98 | SELECT datname, 99 | SUM(plan_user_time) AS plan_user_time, 100 | SUM(plan_system_time) AS plan_system_time, 101 | SUM(plan_minflts) AS plan_minflts, 102 | SUM(plan_majflts) AS plan_majflts, 103 | SUM(plan_nswaps) AS plan_nswaps, 104 | SUM(plan_reads) AS plan_reads, 105 | SUM(plan_reads_blks) AS plan_reads_blks, 106 | SUM(plan_writes) AS plan_writes, 107 | SUM(plan_writes_blks) AS plan_writes_blks, 108 | SUM(plan_msgsnds) AS plan_msgsnds, 109 | SUM(plan_msgrcvs) AS plan_msgrcvs, 110 | SUM(plan_nsignals) AS plan_nsignals, 111 | SUM(plan_nvcsws) AS plan_nvcsws, 112 | SUM(plan_nivcsws) AS plan_nivcsws, 113 | SUM(exec_user_time) AS exec_user_time, 114 | SUM(exec_system_time) AS exec_system_time, 115 | SUM(exec_minflts) AS exec_minflts, 116 | SUM(exec_majflts) AS exec_majflts, 117 | SUM(exec_nswaps) AS exec_nswaps, 118 | SUM(exec_reads) AS exec_reads, 119 | SUM(exec_reads_blks) AS exec_reads_blks, 120 | SUM(exec_writes) AS exec_writes, 121 | SUM(exec_writes_blks) AS exec_writes_blks, 122 | SUM(exec_msgsnds) AS exec_msgsnds, 123 | SUM(exec_msgrcvs) AS exec_msgrcvs, 124 | SUM(exec_nsignals) AS exec_nsignals, 125 | SUM(exec_nvcsws) AS exec_nvcsws, 126 | SUM(exec_nivcsws) AS exec_nivcsws, 127 | MIN(stats_since) AS stats_since 128 | FROM pg_stat_kcache_detail 129 | WHERE top IS TRUE 130 | GROUP BY datname; 131 | GRANT SELECT ON pg_stat_kcache TO public; 132 | -------------------------------------------------------------------------------- /pg_stat_kcache.c: -------------------------------------------------------------------------------- 1 | /*--------------- 2 | * pg_stat_kcache 3 | * 4 | * Provides basic statistics about real I/O done by the filesystem layer. 5 | * This way, calculating a real hit-ratio is doable. Also provides basis 6 | * statistics about CPU usage. 7 | * 8 | * Large portions of code freely inspired by pg_stat_plans. Thanks to Peter 9 | * Geoghegan for this great extension. 10 | * 11 | * This program is open source, licensed under the PostgreSQL license. 12 | * For license terms, see the LICENSE file. 13 | * 14 | */ 15 | 16 | #include "postgres.h" 17 | 18 | #include 19 | 20 | /* 21 | * pg16 removed the configure probe for getrusage. Simply define it for all 22 | * platforms except Windows to keep existing code backward compatible. 23 | */ 24 | #if PG_VERSION_NUM >= 160000 25 | #ifndef WIN32 26 | #define HAVE_GETRUSAGE 27 | #endif /* HAVE_GETRUSAGE */ 28 | #endif /* pg16+ */ 29 | 30 | #if PG_VERSION_NUM < 160000 31 | #ifdef HAVE_SYS_RESOURCE_H 32 | #include 33 | #include 34 | #endif /* HAVE_SYS_RESOURCE_H */ 35 | 36 | #ifndef HAVE_GETRUSAGE 37 | #include "rusagestub.h" 38 | #endif /* !HAVE_GETRUSAGE */ 39 | #endif /* pg16- */ 40 | 41 | #include "access/hash.h" 42 | #if PG_VERSION_NUM >= 90600 43 | #include "access/parallel.h" 44 | #endif 45 | #include "executor/executor.h" 46 | #include "funcapi.h" 47 | #include "miscadmin.h" 48 | #if PG_VERSION_NUM >= 130000 49 | #include "optimizer/planner.h" 50 | #endif 51 | #include "pgstat.h" 52 | #if PG_VERSION_NUM >= 90600 53 | #include "postmaster/autovacuum.h" 54 | #endif 55 | #if PG_VERSION_NUM >= 120000 56 | #include "replication/walsender.h" 57 | #endif 58 | #include "storage/fd.h" 59 | #include "storage/ipc.h" 60 | #include "storage/spin.h" 61 | #include "utils/builtins.h" 62 | #include "utils/guc.h" 63 | #if PG_VERSION_NUM >= 160000 64 | #include "utils/pg_rusage.h" 65 | #endif 66 | #include "utils/timestamp.h" 67 | 68 | #include "pg_stat_kcache.h" 69 | 70 | PG_MODULE_MAGIC; 71 | 72 | #if PG_VERSION_NUM >= 90300 73 | #define PGSK_DUMP_FILE "pg_stat/pg_stat_kcache.stat" 74 | #else 75 | #define PGSK_DUMP_FILE "global/pg_stat_kcache.stat" 76 | #endif 77 | 78 | /* In PostgreSQL 11, queryid becomes a uint64 internally. 79 | */ 80 | #if PG_VERSION_NUM >= 110000 81 | typedef uint64 pgsk_queryid; 82 | #else 83 | typedef uint32 pgsk_queryid; 84 | #endif 85 | 86 | #define USAGE_INCREASE (1.0) 87 | #define USAGE_DECREASE_FACTOR (0.99) /* decreased every pgsk_entry_dealloc */ 88 | #define USAGE_DEALLOC_PERCENT 5 /* free this % of entries at once */ 89 | #define USAGE_INIT (1.0) /* including initial planning */ 90 | 91 | #define TIMEVAL_DIFF(start, end) ((double) end.tv_sec + (double) end.tv_usec / 1000000.0) \ 92 | - ((double) start.tv_sec + (double) start.tv_usec / 1000000.0) 93 | 94 | #if PG_VERSION_NUM < 170000 95 | #define MyProcNumber MyBackendId 96 | #define ParallelLeaderProcNumber ParallelLeaderBackendId 97 | #endif 98 | 99 | #if PG_VERSION_NUM < 140000 100 | #define ParallelLeaderBackendId ParallelMasterBackendId 101 | #endif 102 | 103 | #define PGSK_MAX_NESTED_LEVEL 64 104 | 105 | /* 106 | * Extension version number, for supporting older extension versions' objects 107 | */ 108 | typedef enum pgskVersion 109 | { 110 | PGSK_V2_0 = 0, 111 | PGSK_V2_1, 112 | PGSK_V2_2, 113 | PGSK_V2_3 114 | } pgskVersion; 115 | 116 | /* Magic number identifying the stats file format */ 117 | static const uint32 PGSK_FILE_HEADER = 0x20240914; 118 | 119 | static struct rusage exec_rusage_start[PGSK_MAX_NESTED_LEVEL]; 120 | #if PG_VERSION_NUM >= 130000 121 | static struct rusage plan_rusage_start[PGSK_MAX_NESTED_LEVEL]; 122 | #endif 123 | 124 | static int pgsk_max = 0; /* max #queries to store. pg_stat_statements.max is used */ 125 | 126 | /* 127 | * Hashtable key that defines the identity of a hashtable entry. We use the 128 | * same hash as pg_stat_statements 129 | */ 130 | typedef struct pgskHashKey 131 | { 132 | Oid userid; /* user OID */ 133 | Oid dbid; /* database OID */ 134 | pgsk_queryid queryid; /* query identifier */ 135 | bool top; /* whether statement is top level */ 136 | } pgskHashKey; 137 | 138 | /* 139 | * Statistics per database 140 | */ 141 | typedef struct pgskEntry 142 | { 143 | pgskHashKey key; /* hash key of entry - MUST BE FIRST */ 144 | pgskCounters counters[PGSK_NUMKIND]; /* statistics for this query */ 145 | slock_t mutex; /* protects the counters only */ 146 | TimestampTz stats_since; /* timestamp of entry allocation */ 147 | } pgskEntry; 148 | 149 | /* 150 | * Global shared state 151 | */ 152 | typedef struct pgskSharedState 153 | { 154 | LWLock *lock; /* protects hashtable search/modification */ 155 | #if PG_VERSION_NUM >= 90600 156 | pgsk_queryid queryids[FLEXIBLE_ARRAY_MEMBER]; /* queryid info for 157 | parallel leaders */ 158 | #endif 159 | } pgskSharedState; 160 | 161 | /*---- Local variables ----*/ 162 | 163 | /* Current nesting depth of planner/ExecutorRun/ProcessUtility calls */ 164 | static int nesting_level = 0; 165 | 166 | 167 | /* saved hook address in case of unload */ 168 | #if PG_VERSION_NUM >= 150000 169 | static shmem_request_hook_type prev_shmem_request_hook = NULL; 170 | #endif 171 | static shmem_startup_hook_type prev_shmem_startup_hook = NULL; 172 | #if PG_VERSION_NUM >= 130000 173 | static planner_hook_type prev_planner_hook = NULL; 174 | #endif 175 | static ExecutorStart_hook_type prev_ExecutorStart = NULL; 176 | static ExecutorRun_hook_type prev_ExecutorRun = NULL; 177 | static ExecutorFinish_hook_type prev_ExecutorFinish = NULL; 178 | static ExecutorEnd_hook_type prev_ExecutorEnd = NULL; 179 | 180 | /* Links to shared memory state */ 181 | static pgskSharedState *pgsk = NULL; 182 | static HTAB *pgsk_hash = NULL; 183 | 184 | /*---- HOOK variables ----*/ 185 | 186 | pgsk_counters_hook_type pgsk_counters_hook = NULL; 187 | 188 | /*---- GUC variables ----*/ 189 | 190 | typedef enum 191 | { 192 | PGSK_TRACK_NONE, /* track no statements */ 193 | PGSK_TRACK_TOP, /* only top level statements */ 194 | PGSK_TRACK_ALL /* all statements, including nested ones */ 195 | } PGSKTrackLevel; 196 | 197 | static const struct config_enum_entry pgs_track_options[] = 198 | { 199 | {"none", PGSK_TRACK_NONE, false}, 200 | {"top", PGSK_TRACK_TOP, false}, 201 | {"all", PGSK_TRACK_ALL, false}, 202 | {NULL, 0, false} 203 | }; 204 | 205 | static int pgsk_track = PGSK_TRACK_TOP; /* tracking level */ 206 | #if PG_VERSION_NUM >= 130000 207 | static bool pgsk_track_planning = false; /* whether to track planning duration */ 208 | #endif 209 | 210 | #define pgsk_enabled(level) \ 211 | ((pgsk_track == PGSK_TRACK_ALL && (level) < PGSK_MAX_NESTED_LEVEL) || \ 212 | (pgsk_track == PGSK_TRACK_TOP && (level) == 0)) 213 | 214 | /*--- Functions --- */ 215 | 216 | void _PG_init(void); 217 | 218 | extern PGDLLEXPORT Datum pg_stat_kcache_reset(PG_FUNCTION_ARGS); 219 | extern PGDLLEXPORT Datum pg_stat_kcache(PG_FUNCTION_ARGS); 220 | extern PGDLLEXPORT Datum pg_stat_kcache_2_1(PG_FUNCTION_ARGS); 221 | extern PGDLLEXPORT Datum pg_stat_kcache_2_2(PG_FUNCTION_ARGS); 222 | extern PGDLLEXPORT Datum pg_stat_kcache_2_3(PG_FUNCTION_ARGS); 223 | 224 | PG_FUNCTION_INFO_V1(pg_stat_kcache_reset); 225 | PG_FUNCTION_INFO_V1(pg_stat_kcache); 226 | PG_FUNCTION_INFO_V1(pg_stat_kcache_2_1); 227 | PG_FUNCTION_INFO_V1(pg_stat_kcache_2_2); 228 | PG_FUNCTION_INFO_V1(pg_stat_kcache_2_3); 229 | 230 | static void pg_stat_kcache_internal(FunctionCallInfo fcinfo, pgskVersion 231 | api_version); 232 | 233 | static void pgsk_setmax(void); 234 | static Size pgsk_memsize(void); 235 | 236 | #if PG_VERSION_NUM >= 150000 237 | static void pgsk_shmem_request(void); 238 | #endif 239 | static void pgsk_shmem_startup(void); 240 | static void pgsk_shmem_shutdown(int code, Datum arg); 241 | #if PG_VERSION_NUM >= 130000 242 | static PlannedStmt *pgsk_planner(Query *parse, 243 | const char *query_string, 244 | int cursorOptions, 245 | ParamListInfo boundParams); 246 | #endif 247 | static void pgsk_ExecutorStart(QueryDesc *queryDesc, int eflags); 248 | static void pgsk_ExecutorRun(QueryDesc *queryDesc, 249 | ScanDirection direction, 250 | #if PG_VERSION_NUM >= 90600 251 | uint64 count 252 | #else 253 | long count 254 | #endif 255 | #if PG_VERSION_NUM >= 100000 && PG_VERSION_NUM < 180000 256 | , bool execute_once 257 | #endif 258 | ); 259 | static void pgsk_ExecutorFinish(QueryDesc *queryDesc); 260 | static void pgsk_ExecutorEnd(QueryDesc *queryDesc); 261 | static pgskEntry *pgsk_entry_alloc(pgskHashKey *key); 262 | static void pgsk_entry_dealloc(void); 263 | static void pgsk_entry_reset(void); 264 | static void pgsk_entry_store(pgsk_queryid queryId, pgskStoreKind kind, 265 | pgskCounters counters); 266 | static uint32 pgsk_hash_fn(const void *key, Size keysize); 267 | static int pgsk_match_fn(const void *key1, const void *key2, Size keysize); 268 | 269 | 270 | static bool pgsk_assign_linux_hz_check_hook(int *newval, void **extra, GucSource source); 271 | static void pgsk_compute_counters(pgskCounters *counters, 272 | struct rusage *rusage_start, 273 | struct rusage *rusage_end, 274 | QueryDesc *queryDesc); 275 | #if PG_VERSION_NUM >= 90600 276 | static Size pgsk_queryids_array_size(void); 277 | static void pgsk_set_queryid(pgsk_queryid queryid); 278 | #endif 279 | 280 | static int pgsk_linux_hz = 0; 281 | 282 | void 283 | _PG_init(void) 284 | { 285 | if (!process_shared_preload_libraries_in_progress) 286 | { 287 | elog(ERROR, "This module can only be loaded via shared_preload_libraries"); 288 | return; 289 | } 290 | 291 | DefineCustomIntVariable("pg_stat_kcache.linux_hz", 292 | "Inform pg_stat_kcache of the linux CONFIG_HZ config option", 293 | "This is used by pg_stat_kcache to compensate for sampling errors " 294 | "in getrusage due to the kernel adhering to its ticks. The default value, -1, " 295 | "tries to guess it at startup. ", 296 | &pgsk_linux_hz, 297 | -1, 298 | -1, 299 | INT_MAX, 300 | PGC_USERSET, 301 | 0, 302 | pgsk_assign_linux_hz_check_hook, 303 | NULL, 304 | NULL); 305 | 306 | DefineCustomEnumVariable("pg_stat_kcache.track", 307 | "Selects which statements are tracked by pg_stat_kcache.", 308 | NULL, 309 | &pgsk_track, 310 | PGSK_TRACK_TOP, 311 | pgs_track_options, 312 | PGC_SUSET, 313 | 0, 314 | NULL, 315 | NULL, 316 | NULL); 317 | 318 | #if PG_VERSION_NUM >= 130000 319 | DefineCustomBoolVariable("pg_stat_kcache.track_planning", 320 | "Selects whether planning duration is tracked by pg_stat_cache.", 321 | NULL, 322 | &pgsk_track_planning, 323 | false, 324 | PGC_SUSET, 325 | 0, 326 | NULL, 327 | NULL, 328 | NULL); 329 | #endif 330 | 331 | EmitWarningsOnPlaceholders("pg_stat_kcache"); 332 | 333 | /* set pgsk_max if needed */ 334 | pgsk_setmax(); 335 | /* 336 | * If you change code here, don't forget to also report the modifications 337 | * in pgsk_shmem_request() for pg15 and later. 338 | */ 339 | #if PG_VERSION_NUM < 150000 340 | RequestAddinShmemSpace(pgsk_memsize()); 341 | #if PG_VERSION_NUM >= 90600 342 | RequestNamedLWLockTranche("pg_stat_kcache", 1); 343 | #else 344 | RequestAddinLWLocks(1); 345 | #endif /* pg 9.6+ */ 346 | #endif /* pg 15- */ 347 | 348 | /* Install hook */ 349 | #if PG_VERSION_NUM >= 150000 350 | prev_shmem_request_hook = shmem_request_hook; 351 | shmem_request_hook = pgsk_shmem_request; 352 | #endif 353 | prev_shmem_startup_hook = shmem_startup_hook; 354 | shmem_startup_hook = pgsk_shmem_startup; 355 | #if PG_VERSION_NUM >= 130000 356 | prev_planner_hook = planner_hook; 357 | planner_hook = pgsk_planner; 358 | #endif 359 | prev_ExecutorStart = ExecutorStart_hook; 360 | ExecutorStart_hook = pgsk_ExecutorStart; 361 | prev_ExecutorRun = ExecutorRun_hook; 362 | ExecutorRun_hook = pgsk_ExecutorRun; 363 | prev_ExecutorFinish = ExecutorFinish_hook; 364 | ExecutorFinish_hook = pgsk_ExecutorFinish; 365 | prev_ExecutorEnd = ExecutorEnd_hook; 366 | ExecutorEnd_hook = pgsk_ExecutorEnd; 367 | } 368 | 369 | static bool 370 | pgsk_assign_linux_hz_check_hook(int *newval, void **extra, GucSource source) 371 | { 372 | int val = *newval; 373 | struct rusage myrusage; 374 | struct timeval previous_value; 375 | 376 | /* In that case we try to guess it */ 377 | if (val == -1) 378 | { 379 | elog(LOG, "Auto detecting pg_stat_kcache.linux_hz parameter..."); 380 | getrusage(RUSAGE_SELF, &myrusage); 381 | previous_value = myrusage.ru_utime; 382 | while (myrusage.ru_utime.tv_usec == previous_value.tv_usec && 383 | myrusage.ru_utime.tv_sec == previous_value.tv_sec) 384 | { 385 | getrusage(RUSAGE_SELF, &myrusage); 386 | } 387 | *newval = (int) (1 / ((myrusage.ru_utime.tv_sec - previous_value.tv_sec) + 388 | (myrusage.ru_utime.tv_usec - previous_value.tv_usec) / 1000000.)); 389 | elog(LOG, "pg_stat_kcache.linux_hz is set to %d", *newval); 390 | } 391 | return true; 392 | } 393 | 394 | static void 395 | pgsk_compute_counters(pgskCounters *counters, 396 | struct rusage *rusage_start, 397 | struct rusage *rusage_end, 398 | QueryDesc *queryDesc) 399 | { 400 | /* Compute CPU time delta */ 401 | counters->utime = TIMEVAL_DIFF(rusage_start->ru_utime, rusage_end->ru_utime); 402 | counters->stime = TIMEVAL_DIFF(rusage_start->ru_stime, rusage_end->ru_stime); 403 | 404 | if (queryDesc && queryDesc->totaltime) 405 | { 406 | /* Make sure stats accumulation is done */ 407 | InstrEndLoop(queryDesc->totaltime); 408 | 409 | /* 410 | * We only consider values greater than 3 * linux tick, otherwise the 411 | * bias is too big 412 | */ 413 | if (queryDesc->totaltime->total < (3. / pgsk_linux_hz)) 414 | { 415 | counters->stime = 0; 416 | counters->utime = queryDesc->totaltime->total; 417 | } 418 | } 419 | 420 | #ifdef HAVE_GETRUSAGE 421 | /* Compute the rest of the counters */ 422 | counters->minflts = rusage_end->ru_minflt - rusage_start->ru_minflt; 423 | counters->majflts = rusage_end->ru_majflt - rusage_start->ru_majflt; 424 | counters->nswaps = rusage_end->ru_nswap - rusage_start->ru_nswap; 425 | counters->reads = rusage_end->ru_inblock - rusage_start->ru_inblock; 426 | counters->writes = rusage_end->ru_oublock - rusage_start->ru_oublock; 427 | counters->msgsnds = rusage_end->ru_msgsnd - rusage_start->ru_msgsnd; 428 | counters->msgrcvs = rusage_end->ru_msgrcv - rusage_start->ru_msgrcv; 429 | counters->nsignals = rusage_end->ru_nsignals - rusage_start->ru_nsignals; 430 | counters->nvcsws = rusage_end->ru_nvcsw - rusage_start->ru_nvcsw; 431 | counters->nivcsws = rusage_end->ru_nivcsw - rusage_start->ru_nivcsw; 432 | #endif 433 | } 434 | 435 | #if PG_VERSION_NUM >= 90600 436 | static void 437 | pgsk_set_queryid(pgsk_queryid queryid) 438 | { 439 | /* Only the leader knows the queryid. */ 440 | Assert(!IsParallelWorker()); 441 | 442 | pgsk->queryids[MyProcNumber] = queryid; 443 | } 444 | #endif 445 | 446 | #if PG_VERSION_NUM >= 150000 447 | /* 448 | * Request additional shared memory resources. 449 | * 450 | * If you change code here, don't forget to also report the modifications in 451 | * _PG_init() for pg14 and below. 452 | */ 453 | static void 454 | pgsk_shmem_request(void) 455 | { 456 | if (prev_shmem_request_hook) 457 | prev_shmem_request_hook(); 458 | 459 | RequestAddinShmemSpace(pgsk_memsize()); 460 | RequestNamedLWLockTranche("pg_stat_kcache", 1); 461 | } 462 | #endif 463 | 464 | static void 465 | pgsk_shmem_startup(void) 466 | { 467 | bool found; 468 | HASHCTL info; 469 | FILE *file; 470 | int i; 471 | uint32 header; 472 | int32 num; 473 | pgskEntry *buffer = NULL; 474 | 475 | if (prev_shmem_startup_hook) 476 | prev_shmem_startup_hook(); 477 | 478 | /* reset in case this is a restart within the postmaster */ 479 | pgsk = NULL; 480 | 481 | /* Create or attach to the shared memory state */ 482 | LWLockAcquire(AddinShmemInitLock, LW_EXCLUSIVE); 483 | 484 | /* global access lock */ 485 | pgsk = ShmemInitStruct("pg_stat_kcache", 486 | (sizeof(pgskSharedState) 487 | #if PG_VERSION_NUM >= 90600 488 | + pgsk_queryids_array_size() 489 | #endif 490 | ), 491 | &found); 492 | 493 | if (!found) 494 | { 495 | /* First time through ... */ 496 | #if PG_VERSION_NUM >= 90600 497 | pgsk->lock = &(GetNamedLWLockTranche("pg_stat_kcache"))->lock; 498 | #else 499 | pgsk->lock = LWLockAssign(); 500 | #endif 501 | } 502 | 503 | /* set pgsk_max if needed */ 504 | pgsk_setmax(); 505 | 506 | memset(&info, 0, sizeof(info)); 507 | info.keysize = sizeof(pgskHashKey); 508 | info.entrysize = sizeof(pgskEntry); 509 | info.hash = pgsk_hash_fn; 510 | info.match = pgsk_match_fn; 511 | 512 | /* allocate stats shared memory hash */ 513 | pgsk_hash = ShmemInitHash("pg_stat_kcache hash", 514 | pgsk_max, pgsk_max, 515 | &info, 516 | HASH_ELEM | HASH_FUNCTION | HASH_COMPARE); 517 | 518 | LWLockRelease(AddinShmemInitLock); 519 | 520 | if (!IsUnderPostmaster) 521 | on_shmem_exit(pgsk_shmem_shutdown, (Datum) 0); 522 | 523 | /* 524 | * Done if some other process already completed our initialization. 525 | */ 526 | if (found) 527 | return; 528 | 529 | /* Load stat file, don't care about locking */ 530 | file = AllocateFile(PGSK_DUMP_FILE, PG_BINARY_R); 531 | if (file == NULL) 532 | { 533 | if (errno == ENOENT) 534 | return; /* ignore not-found error */ 535 | goto error; 536 | } 537 | 538 | /* check is header is valid */ 539 | if (fread(&header, sizeof(uint32), 1, file) != 1 || 540 | header != PGSK_FILE_HEADER) 541 | goto error; 542 | 543 | /* get number of entries */ 544 | if (fread(&num, sizeof(int32), 1, file) != 1) 545 | goto error; 546 | 547 | for (i = 0; i < num ; i++) 548 | { 549 | pgskEntry temp; 550 | pgskEntry *entry; 551 | 552 | if (fread(&temp, sizeof(pgskEntry), 1, file) != 1) 553 | goto error; 554 | 555 | /* make the hashtable entry (discards old entries if too many) */ 556 | entry = pgsk_entry_alloc(&temp.key); 557 | 558 | /* copy in the actual stats */ 559 | entry->counters[0] = temp.counters[0]; 560 | entry->counters[1] = temp.counters[1]; 561 | entry->stats_since = temp.stats_since; 562 | /* don't initialize spinlock, already done */ 563 | } 564 | 565 | FreeFile(file); 566 | 567 | /* 568 | * Remove the file so it's not included in backups/replication slaves, 569 | * etc. A new file will be written on next shutdown. 570 | */ 571 | unlink(PGSK_DUMP_FILE); 572 | 573 | return; 574 | 575 | error: 576 | ereport(LOG, 577 | (errcode_for_file_access(), 578 | errmsg("could not read pg_stat_kcache file \"%s\": %m", 579 | PGSK_DUMP_FILE))); 580 | if (buffer) 581 | pfree(buffer); 582 | if (file) 583 | FreeFile(file); 584 | /* delete bogus file, don't care of errors in this case */ 585 | unlink(PGSK_DUMP_FILE); 586 | } 587 | 588 | /* 589 | * shmem_shutdown hook: dump statistics into file. 590 | * 591 | */ 592 | static void 593 | pgsk_shmem_shutdown(int code, Datum arg) 594 | { 595 | FILE *file; 596 | HASH_SEQ_STATUS hash_seq; 597 | int32 num_entries; 598 | pgskEntry *entry; 599 | 600 | /* Don't try to dump during a crash. */ 601 | if (code) 602 | return; 603 | 604 | if (!pgsk) 605 | return; 606 | 607 | file = AllocateFile(PGSK_DUMP_FILE ".tmp", PG_BINARY_W); 608 | if (file == NULL) 609 | goto error; 610 | 611 | if (fwrite(&PGSK_FILE_HEADER, sizeof(uint32), 1, file) != 1) 612 | goto error; 613 | 614 | num_entries = hash_get_num_entries(pgsk_hash); 615 | 616 | if (fwrite(&num_entries, sizeof(int32), 1, file) != 1) 617 | goto error; 618 | 619 | hash_seq_init(&hash_seq, pgsk_hash); 620 | while ((entry = hash_seq_search(&hash_seq)) != NULL) 621 | { 622 | if (fwrite(entry, sizeof(pgskEntry), 1, file) != 1) 623 | { 624 | /* note: we assume hash_seq_term won't change errno */ 625 | hash_seq_term(&hash_seq); 626 | goto error; 627 | } 628 | } 629 | 630 | if (FreeFile(file)) 631 | { 632 | file = NULL; 633 | goto error; 634 | } 635 | 636 | /* 637 | * Rename file inplace 638 | */ 639 | if (rename(PGSK_DUMP_FILE ".tmp", PGSK_DUMP_FILE) != 0) 640 | ereport(LOG, 641 | (errcode_for_file_access(), 642 | errmsg("could not rename pg_stat_kcache file \"%s\": %m", 643 | PGSK_DUMP_FILE ".tmp"))); 644 | 645 | return; 646 | 647 | error: 648 | ereport(LOG, 649 | (errcode_for_file_access(), 650 | errmsg("could not read pg_stat_kcache file \"%s\": %m", 651 | PGSK_DUMP_FILE))); 652 | 653 | if (file) 654 | FreeFile(file); 655 | unlink(PGSK_DUMP_FILE); 656 | } 657 | 658 | /* 659 | * Retrieve pg_stat_statement.max GUC value and store it into pgsk_max, since 660 | * we want to store the same number of entries as pg_stat_statements. Don't do 661 | * anything if pgsk_max is already set. 662 | */ 663 | static void pgsk_setmax(void) 664 | { 665 | const char *pgss_max; 666 | const char *name = "pg_stat_statements.max"; 667 | 668 | if (pgsk_max != 0) 669 | return; 670 | 671 | pgss_max = GetConfigOption(name, true, false); 672 | 673 | /* 674 | * Retrieving pg_stat_statements.max can fail if pgss is loaded after pgsk 675 | * in shared_preload_libraries. Hint user in case this happens. 676 | */ 677 | if (!pgss_max) 678 | ereport(ERROR, 679 | (errcode(ERRCODE_UNDEFINED_OBJECT), 680 | errmsg("unrecognized configuration parameter \"%s\"", 681 | name), 682 | errhint("make sure pg_stat_statements is loaded,\n" 683 | "and make sure pg_stat_kcache is present after pg_stat_statements" 684 | " in the shared_preload_libraries setting"))); 685 | 686 | pgsk_max = atoi(pgss_max); 687 | } 688 | 689 | static Size pgsk_memsize(void) 690 | { 691 | Size size; 692 | 693 | Assert(pgsk_max != 0); 694 | 695 | size = MAXALIGN(sizeof(pgskSharedState)); 696 | size = add_size(size, hash_estimate_size(pgsk_max, sizeof(pgskEntry))); 697 | #if PG_VERSION_NUM >= 90600 698 | size = add_size(size, MAXALIGN(pgsk_queryids_array_size())); 699 | #endif 700 | 701 | return size; 702 | } 703 | 704 | #if PG_VERSION_NUM >= 90600 705 | static Size 706 | pgsk_queryids_array_size(void) 707 | { 708 | /* 709 | * queryid isn't pushed to parallel workers. We store them in shared mem 710 | * for each query, identified by their BackendId. It therefore needs room 711 | * for all possible backends, plus autovacuum launcher and workers, plus bg 712 | * workers and an extra one since BackendId numerotation starts at 1. 713 | * Starting with pg12, wal senders aren't part of MaxConnections anymore, 714 | * so they need to be accounted for. 715 | */ 716 | #if PG_VERSION_NUM >= 150000 717 | Assert (MaxBackends > 0); 718 | return (sizeof(pgsk_queryid) * (MaxBackends + 1)); 719 | #else 720 | return (sizeof(pgsk_queryid) * (MaxConnections + autovacuum_max_workers + 1 721 | + max_worker_processes 722 | #if PG_VERSION_NUM >= 120000 723 | + max_wal_senders 724 | #endif /* pg12+ */ 725 | + 1)); 726 | #endif /* pg15- */ 727 | } 728 | #endif 729 | 730 | 731 | /* 732 | * support functions 733 | */ 734 | 735 | static void 736 | pgsk_entry_store(pgsk_queryid queryId, pgskStoreKind kind, 737 | pgskCounters counters) 738 | { 739 | volatile pgskEntry *e; 740 | 741 | pgskHashKey key; 742 | pgskEntry *entry; 743 | 744 | /* Safety check... */ 745 | if (!pgsk || !pgsk_hash) 746 | return; 747 | 748 | /* Set up key for hashtable search */ 749 | key.userid = GetUserId(); 750 | key.dbid = MyDatabaseId; 751 | key.queryid = queryId; 752 | key.top = (nesting_level == 0); 753 | 754 | /* Lookup the hash table entry with shared lock. */ 755 | LWLockAcquire(pgsk->lock, LW_SHARED); 756 | 757 | entry = (pgskEntry *) hash_search(pgsk_hash, &key, HASH_FIND, NULL); 758 | 759 | /* Create new entry, if not present */ 760 | if (!entry) 761 | { 762 | /* Need exclusive lock to make a new hashtable entry - promote */ 763 | LWLockRelease(pgsk->lock); 764 | LWLockAcquire(pgsk->lock, LW_EXCLUSIVE); 765 | 766 | /* OK to create a new hashtable entry */ 767 | entry = pgsk_entry_alloc(&key); 768 | } 769 | 770 | /* 771 | * Grab the spinlock while updating the counters (see comment about 772 | * locking rules at the head of the file) 773 | */ 774 | e = (volatile pgskEntry *) entry; 775 | 776 | SpinLockAcquire(&e->mutex); 777 | 778 | e->counters[0].usage += USAGE_INCREASE; 779 | 780 | e->counters[kind].utime += counters.utime; 781 | e->counters[kind].stime += counters.stime; 782 | #ifdef HAVE_GETRUSAGE 783 | e->counters[kind].minflts += counters.minflts; 784 | e->counters[kind].majflts += counters.majflts; 785 | e->counters[kind].nswaps += counters.nswaps; 786 | e->counters[kind].reads += counters.reads; 787 | e->counters[kind].writes += counters.writes; 788 | e->counters[kind].msgsnds += counters.msgsnds; 789 | e->counters[kind].msgrcvs += counters.msgrcvs; 790 | e->counters[kind].nsignals += counters.nsignals; 791 | e->counters[kind].nvcsws += counters.nvcsws; 792 | e->counters[kind].nivcsws += counters.nivcsws; 793 | #endif 794 | 795 | SpinLockRelease(&e->mutex); 796 | 797 | LWLockRelease(pgsk->lock); 798 | } 799 | 800 | /* 801 | * Allocate a new hashtable entry. 802 | * caller must hold an exclusive lock on pgsk->lock 803 | */ 804 | static pgskEntry *pgsk_entry_alloc(pgskHashKey *key) 805 | { 806 | pgskEntry *entry; 807 | bool found; 808 | 809 | /* Make space if needed */ 810 | while (hash_get_num_entries(pgsk_hash) >= pgsk_max) 811 | pgsk_entry_dealloc(); 812 | 813 | /* Find or create an entry with desired hash code */ 814 | entry = (pgskEntry *) hash_search(pgsk_hash, key, HASH_ENTER, &found); 815 | 816 | if (!found) 817 | { 818 | /* New entry, initialize it */ 819 | 820 | /* reset the statistics */ 821 | memset(&entry->counters, 0, sizeof(pgskCounters) * PGSK_NUMKIND); 822 | /* set the appropriate initial usage count */ 823 | entry->counters[0].usage = USAGE_INIT; 824 | /* re-initialize the mutex each time ... we assume no one using it */ 825 | SpinLockInit(&entry->mutex); 826 | entry->stats_since = GetCurrentTimestamp(); 827 | } 828 | 829 | return entry; 830 | } 831 | 832 | /* 833 | * qsort comparator for sorting into increasing usage order 834 | */ 835 | static int 836 | entry_cmp(const void *lhs, const void *rhs) 837 | { 838 | double l_usage = (*(pgskEntry *const *) lhs)->counters[0].usage; 839 | double r_usage = (*(pgskEntry *const *) rhs)->counters[0].usage; 840 | 841 | if (l_usage < r_usage) 842 | return -1; 843 | else if (l_usage > r_usage) 844 | return +1; 845 | else 846 | return 0; 847 | } 848 | 849 | /* 850 | * Deallocate least used entries. 851 | * Caller must hold an exclusive lock on pgsk->lock. 852 | */ 853 | static void 854 | pgsk_entry_dealloc(void) 855 | { 856 | HASH_SEQ_STATUS hash_seq; 857 | pgskEntry **entries; 858 | pgskEntry *entry; 859 | int nvictims; 860 | int i; 861 | 862 | /* 863 | * Sort entries by usage and deallocate USAGE_DEALLOC_PERCENT of them. 864 | * While we're scanning the table, apply the decay factor to the usage 865 | * values. 866 | */ 867 | entries = palloc(hash_get_num_entries(pgsk_hash) * sizeof(pgskEntry *)); 868 | 869 | i = 0; 870 | hash_seq_init(&hash_seq, pgsk_hash); 871 | while ((entry = hash_seq_search(&hash_seq)) != NULL) 872 | { 873 | entries[i++] = entry; 874 | entry->counters[0].usage *= USAGE_DECREASE_FACTOR; 875 | } 876 | 877 | qsort(entries, i, sizeof(pgskEntry *), entry_cmp); 878 | 879 | nvictims = Max(10, i * USAGE_DEALLOC_PERCENT / 100); 880 | nvictims = Min(nvictims, i); 881 | 882 | for (i = 0; i < nvictims; i++) 883 | { 884 | hash_search(pgsk_hash, &entries[i]->key, HASH_REMOVE, NULL); 885 | } 886 | 887 | pfree(entries); 888 | } 889 | 890 | static void pgsk_entry_reset(void) 891 | { 892 | HASH_SEQ_STATUS hash_seq; 893 | pgskEntry *entry; 894 | 895 | LWLockAcquire(pgsk->lock, LW_EXCLUSIVE); 896 | 897 | hash_seq_init(&hash_seq, pgsk_hash); 898 | while ((entry = hash_seq_search(&hash_seq)) != NULL) 899 | { 900 | hash_search(pgsk_hash, &entry->key, HASH_REMOVE, NULL); 901 | } 902 | 903 | LWLockRelease(pgsk->lock); 904 | } 905 | 906 | /* 907 | * Hooks 908 | */ 909 | 910 | #if PG_VERSION_NUM >= 130000 911 | /* 912 | * Planner hook: forward to regular planner, but measure planning time 913 | * if needed. 914 | */ 915 | static PlannedStmt * 916 | pgsk_planner(Query *parse, 917 | const char *query_string, 918 | int cursorOptions, 919 | ParamListInfo boundParams) 920 | { 921 | PlannedStmt *result; 922 | 923 | /* We can't process the query if no queryid has been computed. */ 924 | if (pgsk_enabled(nesting_level) 925 | && pgsk_track_planning 926 | && parse->queryId != UINT64CONST(0)) 927 | { 928 | struct rusage *rusage_start = &plan_rusage_start[nesting_level]; 929 | struct rusage rusage_end; 930 | pgskCounters counters; 931 | 932 | /* capture kernel usage stats in rusage_start */ 933 | getrusage(RUSAGE_SELF, rusage_start); 934 | 935 | nesting_level++; 936 | PG_TRY(); 937 | { 938 | if (prev_planner_hook) 939 | result = prev_planner_hook(parse, query_string, cursorOptions, 940 | boundParams); 941 | else 942 | result = standard_planner(parse, query_string, cursorOptions, 943 | boundParams); 944 | nesting_level--; 945 | } 946 | PG_CATCH(); 947 | { 948 | nesting_level--; 949 | PG_RE_THROW(); 950 | } 951 | PG_END_TRY(); 952 | 953 | /* capture kernel usage stats in rusage_end */ 954 | getrusage(RUSAGE_SELF, &rusage_end); 955 | 956 | pgsk_compute_counters(&counters, rusage_start, &rusage_end, NULL); 957 | 958 | /* store current number of block reads and writes */ 959 | pgsk_entry_store(parse->queryId, PGSK_PLAN, counters); 960 | 961 | if (pgsk_counters_hook) 962 | pgsk_counters_hook(&counters, 963 | query_string, 964 | nesting_level, 965 | PGSK_PLAN); 966 | } 967 | else 968 | { 969 | /* 970 | * Even though we're not tracking planning for this statement, we 971 | * must still increment the nesting level, to ensure that functions 972 | * evaluated during planning are not seen as top-level calls. 973 | */ 974 | nesting_level++; 975 | PG_TRY(); 976 | { 977 | if (prev_planner_hook) 978 | result = prev_planner_hook(parse, query_string, cursorOptions, 979 | boundParams); 980 | else 981 | result = standard_planner(parse, query_string, cursorOptions, 982 | boundParams); 983 | nesting_level--; 984 | } 985 | PG_CATCH(); 986 | { 987 | nesting_level--; 988 | PG_RE_THROW(); 989 | } 990 | PG_END_TRY(); 991 | } 992 | 993 | return result; 994 | } 995 | #endif 996 | 997 | static void 998 | pgsk_ExecutorStart (QueryDesc *queryDesc, int eflags) 999 | { 1000 | if (pgsk_enabled(nesting_level)) 1001 | { 1002 | struct rusage *rusage_start = &exec_rusage_start[nesting_level]; 1003 | 1004 | /* capture kernel usage stats in rusage_start */ 1005 | getrusage(RUSAGE_SELF, rusage_start); 1006 | 1007 | #if PG_VERSION_NUM >= 90600 1008 | /* Save the queryid so parallel worker can retrieve it */ 1009 | if (!IsParallelWorker()) 1010 | { 1011 | pgsk_set_queryid(queryDesc->plannedstmt->queryId); 1012 | } 1013 | #endif 1014 | } 1015 | 1016 | /* give control back to PostgreSQL */ 1017 | if (prev_ExecutorStart) 1018 | prev_ExecutorStart(queryDesc, eflags); 1019 | else 1020 | standard_ExecutorStart(queryDesc, eflags); 1021 | } 1022 | 1023 | /* 1024 | * ExecutorRun hook: all we need do is track nesting depth 1025 | */ 1026 | static void 1027 | pgsk_ExecutorRun(QueryDesc *queryDesc, 1028 | ScanDirection direction, 1029 | #if PG_VERSION_NUM >= 90600 1030 | uint64 count 1031 | #else 1032 | long count 1033 | #endif 1034 | #if PG_VERSION_NUM >= 100000 && PG_VERSION_NUM < 180000 1035 | ,bool execute_once 1036 | #endif 1037 | ) 1038 | { 1039 | nesting_level++; 1040 | PG_TRY(); 1041 | { 1042 | if (prev_ExecutorRun) 1043 | #if PG_VERSION_NUM >= 100000 && PG_VERSION_NUM < 180000 1044 | prev_ExecutorRun(queryDesc, direction, count, execute_once); 1045 | #else 1046 | prev_ExecutorRun(queryDesc, direction, count); 1047 | #endif 1048 | else 1049 | #if PG_VERSION_NUM >= 100000 && PG_VERSION_NUM < 180000 1050 | standard_ExecutorRun(queryDesc, direction, count, execute_once); 1051 | #else 1052 | standard_ExecutorRun(queryDesc, direction, count); 1053 | #endif 1054 | nesting_level--; 1055 | } 1056 | PG_CATCH(); 1057 | { 1058 | nesting_level--; 1059 | PG_RE_THROW(); 1060 | } 1061 | PG_END_TRY(); 1062 | } 1063 | 1064 | /* 1065 | * ExecutorFinish hook: all we need do is track nesting depth 1066 | */ 1067 | static void 1068 | pgsk_ExecutorFinish(QueryDesc *queryDesc) 1069 | { 1070 | nesting_level++; 1071 | PG_TRY(); 1072 | { 1073 | if (prev_ExecutorFinish) 1074 | prev_ExecutorFinish(queryDesc); 1075 | else 1076 | standard_ExecutorFinish(queryDesc); 1077 | nesting_level--; 1078 | } 1079 | PG_CATCH(); 1080 | { 1081 | nesting_level--; 1082 | PG_RE_THROW(); 1083 | } 1084 | PG_END_TRY(); 1085 | } 1086 | 1087 | static void 1088 | pgsk_ExecutorEnd (QueryDesc *queryDesc) 1089 | { 1090 | pgsk_queryid queryId; 1091 | struct rusage rusage_end; 1092 | pgskCounters counters; 1093 | 1094 | if (pgsk_enabled(nesting_level)) 1095 | { 1096 | struct rusage *rusage_start = &exec_rusage_start[nesting_level]; 1097 | 1098 | /* capture kernel usage stats in rusage_end */ 1099 | getrusage(RUSAGE_SELF, &rusage_end); 1100 | 1101 | #if PG_VERSION_NUM >= 90600 1102 | if (IsParallelWorker()) 1103 | queryId = pgsk->queryids[ParallelLeaderProcNumber]; 1104 | else 1105 | #endif 1106 | queryId = queryDesc->plannedstmt->queryId; 1107 | 1108 | pgsk_compute_counters(&counters, rusage_start, &rusage_end, queryDesc); 1109 | 1110 | /* store current number of block reads and writes */ 1111 | pgsk_entry_store(queryId, PGSK_EXEC, counters); 1112 | 1113 | if (pgsk_counters_hook) 1114 | pgsk_counters_hook(&counters, 1115 | (const char *)queryDesc->sourceText, 1116 | nesting_level, 1117 | PGSK_EXEC); 1118 | } 1119 | 1120 | /* give control back to PostgreSQL */ 1121 | if (prev_ExecutorEnd) 1122 | prev_ExecutorEnd(queryDesc); 1123 | else 1124 | standard_ExecutorEnd(queryDesc); 1125 | } 1126 | 1127 | /* 1128 | * Calculate hash value for a key 1129 | */ 1130 | static uint32 1131 | pgsk_hash_fn(const void *key, Size keysize) 1132 | { 1133 | const pgskHashKey *k = (const pgskHashKey *) key; 1134 | 1135 | return hash_uint32((uint32) k->userid) ^ 1136 | hash_uint32((uint32) k->dbid) ^ 1137 | hash_uint32((uint32) k->queryid) ^ 1138 | hash_uint32((uint32) k->top); 1139 | } 1140 | 1141 | /* 1142 | * Compare two keys - zero means match 1143 | */ 1144 | static int 1145 | pgsk_match_fn(const void *key1, const void *key2, Size keysize) 1146 | { 1147 | const pgskHashKey *k1 = (const pgskHashKey *) key1; 1148 | const pgskHashKey *k2 = (const pgskHashKey *) key2; 1149 | 1150 | if (k1->userid == k2->userid && 1151 | k1->dbid == k2->dbid && 1152 | k1->queryid == k2->queryid && 1153 | k1->top == k2->top) 1154 | return 0; 1155 | else 1156 | return 1; 1157 | } 1158 | 1159 | /* 1160 | * Reset statistics. 1161 | */ 1162 | PGDLLEXPORT Datum 1163 | pg_stat_kcache_reset(PG_FUNCTION_ARGS) 1164 | { 1165 | if (!pgsk) 1166 | ereport(ERROR, 1167 | (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), 1168 | errmsg("pg_stat_kcache must be loaded via shared_preload_libraries"))); 1169 | 1170 | pgsk_entry_reset(); 1171 | PG_RETURN_VOID(); 1172 | } 1173 | 1174 | PGDLLEXPORT Datum 1175 | pg_stat_kcache(PG_FUNCTION_ARGS) 1176 | { 1177 | pg_stat_kcache_internal(fcinfo, PGSK_V2_0); 1178 | 1179 | return (Datum) 0; 1180 | } 1181 | 1182 | PGDLLEXPORT Datum 1183 | pg_stat_kcache_2_1(PG_FUNCTION_ARGS) 1184 | { 1185 | pg_stat_kcache_internal(fcinfo, PGSK_V2_1); 1186 | 1187 | return (Datum) 0; 1188 | } 1189 | 1190 | PGDLLEXPORT Datum 1191 | pg_stat_kcache_2_2(PG_FUNCTION_ARGS) 1192 | { 1193 | pg_stat_kcache_internal(fcinfo, PGSK_V2_2); 1194 | 1195 | return (Datum) 0; 1196 | } 1197 | 1198 | PGDLLEXPORT Datum 1199 | pg_stat_kcache_2_3(PG_FUNCTION_ARGS) 1200 | { 1201 | pg_stat_kcache_internal(fcinfo, PGSK_V2_3); 1202 | 1203 | return (Datum) 0; 1204 | } 1205 | 1206 | static void 1207 | pg_stat_kcache_internal(FunctionCallInfo fcinfo, pgskVersion api_version) 1208 | { 1209 | ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; 1210 | MemoryContext per_query_ctx; 1211 | MemoryContext oldcontext; 1212 | TupleDesc tupdesc; 1213 | Tuplestorestate *tupstore; 1214 | HASH_SEQ_STATUS hash_seq; 1215 | pgskEntry *entry; 1216 | 1217 | 1218 | if (!pgsk) 1219 | ereport(ERROR, 1220 | (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), 1221 | errmsg("pg_stat_kcache must be loaded via shared_preload_libraries"))); 1222 | /* check to see if caller supports us returning a tuplestore */ 1223 | if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo)) 1224 | ereport(ERROR, 1225 | (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), 1226 | errmsg("set-valued function called in context that cannot accept a set"))); 1227 | if (!(rsinfo->allowedModes & SFRM_Materialize)) 1228 | ereport(ERROR, 1229 | (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), 1230 | errmsg("materialize mode required, but it is not " \ 1231 | "allowed in this context"))); 1232 | 1233 | /* Switch into long-lived context to construct returned data structures */ 1234 | per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; 1235 | oldcontext = MemoryContextSwitchTo(per_query_ctx); 1236 | 1237 | /* Build a tuple descriptor for our result type */ 1238 | if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) 1239 | elog(ERROR, "return type must be a row type"); 1240 | 1241 | tupstore = tuplestore_begin_heap(true, false, work_mem); 1242 | rsinfo->returnMode = SFRM_Materialize; 1243 | rsinfo->setResult = tupstore; 1244 | rsinfo->setDesc = tupdesc; 1245 | 1246 | MemoryContextSwitchTo(oldcontext); 1247 | 1248 | LWLockAcquire(pgsk->lock, LW_SHARED); 1249 | 1250 | hash_seq_init(&hash_seq, pgsk_hash); 1251 | while ((entry = hash_seq_search(&hash_seq)) != NULL) 1252 | { 1253 | Datum values[PG_STAT_KCACHE_COLS]; 1254 | bool nulls[PG_STAT_KCACHE_COLS]; 1255 | volatile pgskCounters *tmp; 1256 | int i = 0; 1257 | int kind, min_kind = 0; 1258 | #ifdef HAVE_GETRUSAGE 1259 | int64 reads, writes; 1260 | #endif 1261 | TimestampTz stats_since; 1262 | 1263 | memset(values, 0, sizeof(values)); 1264 | memset(nulls, 0, sizeof(nulls)); 1265 | 1266 | values[i++] = Int64GetDatum(entry->key.queryid); 1267 | if (api_version >= PGSK_V2_2) 1268 | values[i++] = BoolGetDatum(entry->key.top); 1269 | values[i++] = ObjectIdGetDatum(entry->key.userid); 1270 | values[i++] = ObjectIdGetDatum(entry->key.dbid); 1271 | 1272 | /* planning time (kind == 0) is added in v2.2 */ 1273 | if (api_version < PGSK_V2_2) 1274 | min_kind = 1; 1275 | 1276 | /* copy counters to a local variable to keep locking time short */ 1277 | { 1278 | volatile pgskEntry *e = (volatile pgskEntry *) entry; 1279 | 1280 | SpinLockAcquire(&e->mutex); 1281 | tmp = e->counters; 1282 | stats_since = e->stats_since; 1283 | SpinLockRelease(&e->mutex); 1284 | } 1285 | 1286 | for (kind = min_kind; kind < PGSK_NUMKIND; kind++) 1287 | { 1288 | #ifdef HAVE_GETRUSAGE 1289 | reads = tmp[kind].reads * RUSAGE_BLOCK_SIZE; 1290 | writes = tmp[kind].writes * RUSAGE_BLOCK_SIZE; 1291 | values[i++] = Int64GetDatumFast(reads); 1292 | values[i++] = Int64GetDatumFast(writes); 1293 | #else 1294 | nulls[i++] = true; /* reads */ 1295 | nulls[i++] = true; /* writes */ 1296 | #endif 1297 | values[i++] = Float8GetDatumFast(tmp[kind].utime); 1298 | values[i++] = Float8GetDatumFast(tmp[kind].stime); 1299 | if (api_version >= PGSK_V2_1) 1300 | { 1301 | #ifdef HAVE_GETRUSAGE 1302 | values[i++] = Int64GetDatumFast(tmp[kind].minflts); 1303 | values[i++] = Int64GetDatumFast(tmp[kind].majflts); 1304 | values[i++] = Int64GetDatumFast(tmp[kind].nswaps); 1305 | values[i++] = Int64GetDatumFast(tmp[kind].msgsnds); 1306 | values[i++] = Int64GetDatumFast(tmp[kind].msgrcvs); 1307 | values[i++] = Int64GetDatumFast(tmp[kind].nsignals); 1308 | values[i++] = Int64GetDatumFast(tmp[kind].nvcsws); 1309 | values[i++] = Int64GetDatumFast(tmp[kind].nivcsws); 1310 | #else 1311 | nulls[i++] = true; /* minflts */ 1312 | nulls[i++] = true; /* majflts */ 1313 | nulls[i++] = true; /* nswaps */ 1314 | nulls[i++] = true; /* msgsnds */ 1315 | nulls[i++] = true; /* msgrcvs */ 1316 | nulls[i++] = true; /* nsignals */ 1317 | nulls[i++] = true; /* nvcsws */ 1318 | nulls[i++] = true; /* nivcsws */ 1319 | #endif 1320 | } 1321 | } 1322 | if (api_version >= PGSK_V2_3) 1323 | values[i++] = TimestampTzGetDatum(stats_since); 1324 | 1325 | Assert(i == (api_version == PGSK_V2_0 ? PG_STAT_KCACHE_COLS_V2_0 : 1326 | api_version == PGSK_V2_1 ? PG_STAT_KCACHE_COLS_V2_1 : 1327 | api_version == PGSK_V2_2 ? PG_STAT_KCACHE_COLS_V2_2 : 1328 | api_version == PGSK_V2_3 ? PG_STAT_KCACHE_COLS_V2_3 : 1329 | -1 /* fail if you forget to update this assert */ )); 1330 | 1331 | tuplestore_putvalues(tupstore, tupdesc, values, nulls); 1332 | } 1333 | 1334 | LWLockRelease(pgsk->lock); 1335 | } 1336 | -------------------------------------------------------------------------------- /pg_stat_kcache.control: -------------------------------------------------------------------------------- 1 | # pg_stat_kcache extension 2 | comment = 'Kernel statistics gathering' 3 | default_version = '2.3.0' 4 | requires = 'pg_stat_statements' 5 | module_pathname = '$libdir/pg_stat_kcache' 6 | relocatable = true 7 | -------------------------------------------------------------------------------- /pg_stat_kcache.h: -------------------------------------------------------------------------------- 1 | #ifndef PGSK_H_ 2 | #define PGSK_H_ 3 | 4 | #define PG_STAT_KCACHE_COLS_V2_0 7 5 | #define PG_STAT_KCACHE_COLS_V2_1 15 6 | #define PG_STAT_KCACHE_COLS_V2_2 28 7 | #define PG_STAT_KCACHE_COLS_V2_3 29 8 | #define PG_STAT_KCACHE_COLS 29 /* maximum of above */ 9 | 10 | /* ru_inblock block size is 512 bytes with Linux 11 | * see http://lkml.indiana.edu/hypermail/linux/kernel/0703.2/0937.html 12 | */ 13 | #define RUSAGE_BLOCK_SIZE 512 /* Size of a block for getrusage() */ 14 | 15 | /* 16 | * Current getrusage counters. 17 | * 18 | * For platform without getrusage support, we rely on postgres implementation 19 | * defined in rusagestub.h, which only supports user and system time. 20 | * 21 | * Note that the following counters are not maintained on GNU/Linux: 22 | * - ru_nswap 23 | * - ru_msgsnd 24 | * - ru_msgrcv 25 | * - ru_nsignals 26 | */ 27 | typedef struct pgskCounters 28 | { 29 | double usage; /* usage factor */ 30 | /* These fields are always used */ 31 | float8 utime; /* CPU user time */ 32 | float8 stime; /* CPU system time */ 33 | #ifdef HAVE_GETRUSAGE 34 | /* These fields are only used for platform with HAVE_GETRUSAGE defined */ 35 | int64 minflts; /* page reclaims (soft page faults) */ 36 | int64 majflts; /* page faults (hard page faults) */ 37 | int64 nswaps; /* swaps */ 38 | int64 reads; /* Physical block reads */ 39 | int64 writes; /* Physical block writes */ 40 | int64 msgsnds; /* IPC messages sent */ 41 | int64 msgrcvs; /* IPC messages received */ 42 | int64 nsignals; /* signals received */ 43 | int64 nvcsws; /* voluntary context witches */ 44 | int64 nivcsws; /* unvoluntary context witches */ 45 | #endif 46 | } pgskCounters; 47 | 48 | typedef enum pgskStoreKind 49 | { 50 | /* 51 | * PGSS_PLAN and PGSS_EXEC must be respectively 0 and 1 as they're used to 52 | * reference the underlying values in the arrays in the Counters struct, 53 | * and this order is required in pg_stat_statements_internal(). 54 | */ 55 | PGSK_PLAN = 0, 56 | PGSK_EXEC, 57 | 58 | PGSK_NUMKIND /* Must be last value of this enum */ 59 | } pgskStoreKind; 60 | 61 | /* Hook for extensions to use pgskCounters right after calculation. */ 62 | typedef void (*pgsk_counters_hook_type) ( 63 | pgskCounters *counters, 64 | const char *query_string, 65 | int level, 66 | pgskStoreKind kind); 67 | extern PGDLLIMPORT pgsk_counters_hook_type pgsk_counters_hook; 68 | 69 | #endif 70 | -------------------------------------------------------------------------------- /test/sql/01_basic.sql: -------------------------------------------------------------------------------- 1 | CREATE EXTENSION pg_stat_statements; 2 | CREATE EXTENSION pg_stat_kcache; 3 | 4 | -- first make sure that catcache is loaded to avoid physical reads 5 | SELECT count(*) >= 0 FROM pg_stat_kcache; 6 | SELECT pg_stat_kcache_reset(); 7 | 8 | -- dummy query 9 | SELECT 1 AS dummy; 10 | 11 | SELECT count(*) FROM pg_stat_kcache WHERE datname = current_database(); 12 | 13 | SELECT count(*) FROM pg_stat_kcache_detail WHERE datname = current_database() AND (query = 'SELECT $1 AS dummy' OR query = 'SELECT ? AS dummy;'); 14 | 15 | SELECT exec_reads, exec_reads_blks, exec_writes, exec_writes_blks 16 | FROM pg_stat_kcache_detail 17 | WHERE datname = current_database() 18 | AND (query = 'SELECT $1 AS dummy' OR query = 'SELECT ? AS dummy;'); 19 | 20 | -- dummy table 21 | CREATE TABLE test AS SELECT i FROM generate_series(1, 1000) i; 22 | 23 | -- dummy query again 24 | SELECT count(*) FROM test; 25 | 26 | SELECT exec_user_time + exec_system_time > 0 AS cpu_time_ok 27 | FROM pg_stat_kcache_detail 28 | WHERE datname = current_database() 29 | AND query LIKE 'SELECT count(*) FROM test%'; 30 | 31 | -- dummy nested query 32 | SET pg_stat_statements.track = 'all'; 33 | SET pg_stat_statements.track_planning = TRUE; 34 | SET pg_stat_kcache.track = 'all'; 35 | SET pg_stat_kcache.track_planning = TRUE; 36 | SELECT pg_stat_kcache_reset(); 37 | 38 | CREATE OR REPLACE FUNCTION plpgsql_nested() 39 | RETURNS void AS $$ 40 | BEGIN 41 | PERFORM i::text as str from generate_series(1, 100) as i; 42 | PERFORM md5(i::text) as str from generate_series(1, 100) as i; 43 | END 44 | $$ LANGUAGE plpgsql; 45 | 46 | SELECT plpgsql_nested(); 47 | 48 | SELECT COUNT(*) FROM pg_stat_kcache_detail WHERE top IS FALSE; 49 | --------------------------------------------------------------------------------