pax_global_header00006660000000000000000000000064151251506210014510gustar00rootroot0000000000000052 comment=b5fc9040ac128e1d2392e11e79333ca353f5f814 pgsentinel-1.3.1/000077500000000000000000000000001512515062100136625ustar00rootroot00000000000000pgsentinel-1.3.1/.editorconfig000066400000000000000000000002671512515062100163440ustar00rootroot00000000000000root = true [*.{c,h,l,y,pl,pm}] indent_style = tab indent_size = tab tab_width = 4 [*.{sgml,xml}] indent_style = space indent_size = 1 [*.xsl] indent_style = space indent_size = 2 pgsentinel-1.3.1/.gitattributes000066400000000000000000000001351512515062100165540ustar00rootroot00000000000000* text=auto *.c text *.md text *.sql text *.sh text eol=lf *.xml text *.yaml text *.yml text pgsentinel-1.3.1/.gitignore000066400000000000000000000002321512515062100156470ustar00rootroot00000000000000.classpath .settings .project .DS_Store .externalToolBuilders build .*.swp nbproject .idea *.iml *.ipr *.iws *.local.properties target/ /eclipsebin/ *.o pgsentinel-1.3.1/.travis.yml000066400000000000000000000021131512515062100157700ustar00rootroot00000000000000sudo: false language: c before_install: - eval "${MATRIX_EVAL}" before_script: - export PG_DATADIR="/etc/postgresql/${PG_VERSION}/main" - .travis/install_postgresql.sh - .travis/install_pgsentinel.sh - .travis/configure_postgresql.sh - .travis/start_postgresql.sh - test "x$PG_VERSION" != 'xHEAD' || psql -U postgres -c "set password_encryption='scram-sha-256'; create user test with password 'test';" - test "x$PG_VERSION" = 'xHEAD' || psql -U postgres -c "create user test with password 'test';" - psql -U postgres -c "CREATE EXTENSION pgsentinel;" - psql -c 'create database test owner test;' -U postgres script: - true after_failure: - .travis/cat_logs.sh matrix: include: - env: - PG_VERSION=HEAD sudo: required - env: - PG_VERSION=HEAD - MATRIX_EVAL="CC=clang-5.0 && CXX=clang++-5.0" sudo: required addons: apt: sources: - llvm-toolchain-trusty-5.0 packages: - clang-5.0 - env: - PG_VERSION=10 sudo: required # skip install script install: true pgsentinel-1.3.1/.travis/000077500000000000000000000000001512515062100152505ustar00rootroot00000000000000pgsentinel-1.3.1/.travis/cat_logs.sh000077500000000000000000000002561512515062100174050ustar00rootroot00000000000000#!/usr/bin/env bash set -x -e if [ "x${PG_VERSION}" = "xHEAD" ] then sudo tail /tmp/postgres.log else sudo tail /var/log/postgresql/postgresql-${PG_VERSION}-main.log fi pgsentinel-1.3.1/.travis/configure_postgresql.sh000077500000000000000000000005361512515062100220570ustar00rootroot00000000000000#!/usr/bin/env bash set -x -e sudo su postgres -c "echo 'shared_preload_libraries = '\''pg_stat_statements,pgsentinel'\' >> '${PG_DATADIR}/postgresql.conf'" sudo su postgres -c "echo 'track_activity_query_size = 2048' >> '${PG_DATADIR}/postgresql.conf'" sudo su postgres -c "echo 'pg_stat_statements.track = all' >> '${PG_DATADIR}/postgresql.conf'" pgsentinel-1.3.1/.travis/install_git_postgresql.sh000077500000000000000000000022241512515062100224030ustar00rootroot00000000000000#!/usr/bin/env bash set -x -e sudo service postgresql stop sudo apt-get update -qq sudo apt-get remove postgresql libpq-dev libpq5 postgresql-client-common postgresql-common -qq --purge sudo apt-get -y install libxml2 gdb if [[ -z ${POSTGRES_SOURCE_SHA} ]] then git clone --depth=1 https://github.com/postgres/postgres.git cd postgres else git clone https://github.com/postgres/postgres.git cd postgres git checkout ${POSTGRES_SOURCE_SHA} fi # Build PostgreSQL from source if [[ "${COMPILE_PG_WITH_DEBUG_FLAG}" == "Y" ]] then sudo ./configure --enable-debug --with-libxml CFLAGS="-ggdb" else sudo ./configure --with-libxml CFLAGS="-ggdb" fi sudo make && sudo make install sudo ln -sf /usr/local/pgsql/bin/psql /usr/bin/psql # Build contrib from source cd contrib sudo make all && sudo make install #Post compile actions LD_LIBRARY_PATH=/usr/local/pgsql/lib export LD_LIBRARY_PATH sudo /sbin/ldconfig /usr/local/pgsql/lib sudo rm -rf ${PG_DATADIR} sudo mkdir -p ${PG_DATADIR} sudo chmod 777 ${PG_DATADIR} sudo chown -R postgres:postgres ${PG_DATADIR} sudo su postgres -c "/usr/local/pgsql/bin/pg_ctl -D ${PG_DATADIR} -U postgres initdb" pgsentinel-1.3.1/.travis/install_pgsentinel.sh000077500000000000000000000001661512515062100215100ustar00rootroot00000000000000#!/usr/bin/env bash set -x -e export PATH=/usr/local/pgsql/bin:$PATH cd src make sudo env "PATH=$PATH" make install pgsentinel-1.3.1/.travis/install_postgresql.sh000077500000000000000000000025361512515062100215460ustar00rootroot00000000000000#!/usr/bin/env bash # Adapted from https://github.com/dockyard/reefpoints/blob/master/source/posts/2013-03-29-running-postgresql-9-2-on-travis-ci.md set -x -e if [ -z "$PG_VERSION" ] then echo "env PG_VERSION not defined"; elif [ "${PG_VERSION}" = "HEAD" ] then .travis/install_git_postgresql.sh elif [ ! -d "${PG_DATADIR}" ] then sudo apt-get remove postgresql libpq-dev libpq5 postgresql-client-common postgresql-common -qq --purge source /etc/lsb-release echo "deb http://apt.postgresql.org/pub/repos/apt/ $DISTRIB_CODENAME-pgdg main ${PG_VERSION}" > pgdg.list sudo mv pgdg.list /etc/apt/sources.list.d/ wget --quiet -O - https://apt.postgresql.org/pub/repos/apt/ACCC4CF8.asc | sudo apt-key add - sudo apt-get update sudo apt-get -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confnew" install postgresql-${PG_VERSION} postgresql-server-dev-${PG_VERSION} postgresql-contrib-${PG_VERSION} -qq sudo sh -c "echo 'local all postgres trust' > /etc/postgresql/${PG_VERSION}/main/pg_hba.conf" sudo sh -c "echo 'local all all trust' >> /etc/postgresql/${PG_VERSION}/main/pg_hba.conf" sudo sh -c "echo -n 'host all all 127.0.0.1/32 trust' >> /etc/postgresql/${PG_VERSION}/main/pg_hba.conf" sudo service postgresql restart ${PG_VERSION} sudo tail /var/log/postgresql/postgresql-${PG_VERSION}-main.log fi pgsentinel-1.3.1/.travis/start_postgresql.sh000077500000000000000000000006761512515062100212400ustar00rootroot00000000000000#!/usr/bin/env bash set -x -e if [ -z "$PG_VERSION" ] then echo "env PG_VERSION not define"; elif [ "x${PG_VERSION}" = "xHEAD" ] then #Start head postgres sudo su postgres -c "/usr/local/pgsql/bin/pg_ctl -D ${PG_DATADIR} -w -t 300 -c -o '-p 5432' -l /tmp/postgres.log start" sudo tail /tmp/postgres.log else sudo service postgresql restart ${PG_VERSION} sudo tail /var/log/postgresql/postgresql-${PG_VERSION}-main.log fi pgsentinel-1.3.1/LICENSE000066400000000000000000000016331512515062100146720ustar00rootroot00000000000000Copyright (c) 2018-2023, PgSentinel Permission to use, copy, modify, and distribute this software and its documentation for any purpose, without fee, and without a written agreement is hereby granted, provided that the above copyright notice and this paragraph and the following two paragraphs appear in all copies. IN NO EVENT SHALL PgSentinel BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF PgSentinel HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. PgSentinel SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND PgSentinel HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. pgsentinel-1.3.1/Makefile000066400000000000000000000000571512515062100153240ustar00rootroot00000000000000all: $(MAKE) -C src all %: $(MAKE) -C src $@ pgsentinel-1.3.1/README.md000066400000000000000000000233551512515062100151510ustar00rootroot00000000000000`pgsentinel` – sampling active session history ============================================================= [![Build Status](https://travis-ci.org/pgsentinel/pgsentinel.svg?branch=master)](https://travis-ci.org/pgsentinel/pgsentinel) Introduction ------------ PostgreSQL provides session activity. However, in order to gather activity behavior, users have to sample the pg_stat_activity view multiple times. `pgsentinel` is an extension to record active session history and link the activity with query statistics (`pg_stat_statements`). The module must be loaded by adding `pgsentinel` to `shared_preload_libraries` in postgresql.conf, which means a server restart is needed. When `pgsentinel` is enabled, it collects the history of session activity: * It's implemented as an in-memory ring buffer where samples are written with a given (configurable) period. Therefore, the user can see some number of recent samples depending on the history size (configurable). In combination with `pg_stat_statements`, this extension can link the session activity with query statistics. To get more granular queries statistics, `pgsentinel` samples the `pg_stat_statements` view: * at the same time it is sampling the active sessions * only for the queryid associated to an active session (if any) during the sampling `pgsentinel` launches a special background worker for gathering session activities. Availability ------------ `pgsentinel` is implemented as an extension and not available in the default PostgreSQL installation. It is available from [github](https://github.com/pgsentinel/pgsentinel) under the same license as [PostgreSQL License](https://github.com/pgsentinel/pgsentinel/blob/master/LICENSE) and supports PostgreSQL 9.6+. Installation ------------ `pgsentinel` is a PostgreSQL extension which requires PostgreSQL 9.6 or higher. Before the build and install steps, you should ensure the following: * PostgreSQL version is 9.6 or higher. * You have the development package of PostgreSQL installed or you built PostgreSQL from source. * Your `PATH` variable configuration includes `pg_config`, or you've set a value for `PG_CONFIG`. The typical installation procedure may look like: As `pgsentinel` uses the `pg_stat_statements` extension (officially bundled with PostgreSQL) for tracking which queries get executed in your database, add the following entries to your postgres.conf: $ shared_preload_libraries = 'pg_stat_statements,pgsentinel' $ # Increase the max size of the query strings Postgres records $ track_activity_query_size = 2048 $ # Track statements generated by stored procedures as well $ pg_stat_statements.track = all restart the postgresql daemon and create the extension: $ git clone https://github.com/pgsentinel/pgsentinel.git $ cd pgsentinel/src $ make $ sudo make install $ psql DB -c "CREATE EXTENSION pgsentinel;" Usage ----- `pgsentinel` reports the active session history activity through the `pg_active_session_history` view: | Column | Type | Collation | Nullable | Default | | ------------------ | -------------------------- | ----------- | ---------- | --------- | | ash_time | timestamp with time zone | | | | | datid | oid | | | | | datname | text | | | | | pid | integer | | | | | leader_pid | integer | | | | | usesysid | oid | | | | | usename | text | | | | | application_name | text | | | | | client_addr | text | | | | | client_hostname | text | | | | | client_port | integer | | | | | backend_start | timestamp with time zone | | | | | xact_start | timestamp with time zone | | | | | query_start | timestamp with time zone | | | | | state_change | timestamp with time zone | | | | | wait_event_type | text | | | | | wait_event | text | | | | | state | text | | | | | backend_xid | xid | | | | | backend_xmin | xid | | | | | top_level_query | text | | | | | query | text | | | | | cmdtype | text | | | | | queryid | bigint | | | | | backend_type | text | | | | | blockers | integer | | | | | blockerpid | integer | | | | | blocker_state | text | | | | You can see it as samplings of `pg_stat_activity` providing more information: * `ash_time`: the sampling time * `top_level_query`: the top level statement (in case PL/pgSQL is used) * `query`: the statement being executed (not normalised, as it is in `pg_stat_statements`, which means you see parameter values) * `cmdtype`: the statement type (SELECT,UPDATE,INSERT,DELETE,UTILITY,UNKNOWN,NOTHING) * `queryid`: the queryid of the statement which links to pg_stat_statements * `blockers`: the number of blockers * `blockerpid`: the pid of the blocker (if blockers = 1), the pid of one blocker (if blockers > 1) * `blocker_state`: state of the blocker (state of the blockerpid) `pgsentinel` also reports query statistics history through the `pg_stat_statements_history` view: | Column | Type | Collation | Nullable | Default | | ------------------ | -------------------------- | ----------- | ---------- | --------- | | ash_time | timestamp with time zone | | | | | userid | oid | | | | | dbid | oid | | | | | queryid | bigint | | | | | calls | bigint | | | | | total_exec_time | double precision | | | | | rows | bigint | | | | | shared_blks_hit | bigint | | | | | shared_blks_read | bigint | | | | | shared_blks_dirtied | bigint | | | | | shared_blks_written | bigint | | | | | local_blks_hit | bigint | | | | | local_blks_read | bigint | | | | | local_blks_dirtied | bigint | | | | | local_blks_written | bigint | | | | | temp_blks_read | bigint | | | | | temp_blks_written | bigint | | | | | blk_read_time | double precision | | | | | blk_write_time | double precision | | | | | plans | bigint | | | | | total_plan_time | double precision | | | | | wal_records | bigint | | | | | wal_fpi | bigint | | | | | wal_bytes | numeric | | | | The field descriptions are the same as for `pg_stat_statements` (except for the `ash_time` one, which is the time of the active session history sampling). The worker is controlled by the following GUCs: | Parameter name | Data type | Description | Default value | Min value | | ----------------------------------- | --------- | ------------------------------------------- | ------------ | -------- | | pgsentinel_ash.sampling_period | int4 | Period for history sampling in seconds | 1 | 1 | | pgsentinel_ash.max_entries | int4 | Size of pg_active_session_history in-memory ring buffer | 1000 | 1000 | | pgsentinel.db_name | char | database the worker should connect to | postgres | | | pgsentinel_ash.track_idle_trans | boolean | track session in idle in transaction state | false | | | pgsentinel_pgssh.max_entries | int4 | Size of pg_stat_statements_history in-memory ring buffer | 1000 | 1000 | | pgsentinel_pgssh.enable | boolean | enable pg_stat_statements_history | false | | Remark ------------------------- * Some fields may be NULL depending on the version (for example, `leader_pid` is NULL for version <= 13.0...) See how to query the view in this short video ------------- [![Alt text](https://raw.githubusercontent.com/pgsentinel/pg_ash_scripts/master/images/video_pg_ash_scripts.PNG)](https://www.youtube.com/watch?v=WVKzKjlK75U) ### The videos are available on [youtube](https://www.youtube.com/channel/UCGVciSS2YwnPhtHHGB3Ep3A) Contribution ------------ If you're lacking some functionality in `pgsentinel` then you're welcome to make pull requests. Author ------- * Bertrand Drouvot , France, [Twitter](https://twitter.com/BertrandDrouvot) pgsentinel-1.3.1/src/000077500000000000000000000000001512515062100144515ustar00rootroot00000000000000pgsentinel-1.3.1/src/Makefile000066400000000000000000000014361512515062100161150ustar00rootroot00000000000000ifdef DEB_BUILD_GNU_TYPE REGRESS_OPTS =--temp-config=./pgsentinel.conf else REGRESS_OPTS =--temp-config=./pgsentinel.conf --temp-instance=./tmp_check endif REGRESS = pgsentinel-test MODULE_big = pgsentinel OBJS = $(patsubst %.c,%.o,$(wildcard ./*.c)) ifeq ($(CC),gcc) PG_CPPFLAGS = -std=c99 -Wall -Wextra -Werror -Wno-unused-parameter -Wno-uninitialized -Wno-implicit-fallthrough -Iinclude else PG_CPPFLAGS = -std=c99 -Wall -Wextra -Werror -Wno-unused-parameter -Wno-implicit-fallthrough -Iinclude endif EXTRA_CLEAN += $(addprefix ./,*.gcno *.gcda) EXTENSION = pgsentinel DATA = $(wildcard $(EXTENSION)*--*.sql) PGFILEDESC = "pgsentinel - active session history" LDFLAGS_SL += $(filter -lm, $(LIBS)) PG_CONFIG = pg_config PGXS := $(shell $(PG_CONFIG) --pgxs) include $(PGXS) pgsentinel-1.3.1/src/expected/000077500000000000000000000000001512515062100162525ustar00rootroot00000000000000pgsentinel-1.3.1/src/expected/pgsentinel-test.out000066400000000000000000000014611512515062100221320ustar00rootroot00000000000000CREATE EXTENSION pg_stat_statements; CREATE EXTENSION pgsentinel; select pg_sleep(3); pg_sleep ---------- (1 row) select count(*) > 0 AS has_data from pg_active_session_history where queryid in (select queryid from pg_stat_statements); has_data ---------- t (1 row) begin; \! sleep 3 commit; select count(*) > 0 AS has_idle_data from pg_active_session_history where state = 'idle in transaction'; has_idle_data --------------- f (1 row) ALTER SYSTEM SET pgsentinel_ash.track_idle_trans = true; select pg_reload_conf(); pg_reload_conf ---------------- t (1 row) begin; \! sleep 3 commit; select count(*) > 0 AS has_idle_data from pg_active_session_history where state = 'idle in transaction'; has_idle_data --------------- t (1 row) DROP EXTENSION pgsentinel; DROP EXTENSION pg_stat_statements; pgsentinel-1.3.1/src/get_parsedinfo.c000066400000000000000000000143261512515062100176140ustar00rootroot00000000000000/* * get_parsedinfo.c * * Copyright (c) 2018-2026, PgSentinel * * IDENTIFICATION: * https://github.com/pgsentinel/pgsentinel * * This program is open source, licensed under the PostgreSQL license. * For license terms, see the LICENSE file. */ #include "postgres.h" #include "pgsentinel.h" #include "storage/ipc.h" #include "storage/proc.h" #include "miscadmin.h" #include "access/twophase.h" #include "parser/scansup.h" #include "parser/analyze.h" #include "access/hash.h" #include "funcapi.h" #include "utils/builtins.h" #include "commands/extension.h" #include "pgstat.h" #include "postmaster/autovacuum.h" #include "replication/walsender.h" Datum get_parsedinfo(PG_FUNCTION_ARGS); PG_FUNCTION_INFO_V1(get_parsedinfo); /* to create queryid in case of utility statements*/ #if PG_VERSION_NUM >= 110000 static uint64 getparsedinfo_hash64_string(const char *str, int len); #else static uint32 getparsedinfo_hash32_string(const char *str, int len); #endif /* Estimate amount of shared memory needed for proc entry*/ Size proc_entry_memsize(void) { Size size; /* ProcEntryArray */ size = mul_size(sizeof(procEntry), get_max_procs_count()); /* ProEntryQueryBuffer */ size = add_size(size, mul_size(pgstat_track_activity_query_size, get_max_procs_count())); /* ProEntryCmdTypeBuffer */ size = add_size(size, mul_size(NAMEDATALEN, get_max_procs_count())); return size; } /* to create queryid in case of utility statements*/ #if PG_VERSION_NUM >= 110000 static uint64 getparsedinfo_hash64_string(const char *str, int len) { return DatumGetUInt64(hash_any_extended((const unsigned char *) str, len, 0)); } #else static uint32 getparsedinfo_hash32_string(const char *str, int len) { return hash_any((const unsigned char *) str, len); } #endif int get_max_procs_count(void) { int count = 0; count += MaxConnections; count += autovacuum_max_workers; count += max_worker_processes; count += max_wal_senders; count += NUM_AUXILIARY_PROCS; count += max_prepared_xacts; count++; return count; } void #if PG_VERSION_NUM < 140000 getparsedinfo_post_parse_analyze(ParseState *pstate, Query *query) #else getparsedinfo_post_parse_analyze(ParseState *pstate, Query *query, JumbleState *jstate) #endif { if (prev_post_parse_analyze_hook) #if PG_VERSION_NUM < 140000 prev_post_parse_analyze_hook(pstate, query); #else prev_post_parse_analyze_hook(pstate, query, jstate); #endif if (MyProc) { int i = MyProc - ProcGlobal->allProcs; const char *querytext = pstate->p_sourcetext; int minlen; int query_len; #if PG_VERSION_NUM >= 100000 int query_location = query->stmt_location; query_len = query->stmt_len; if (query_location >= 0) { Assert(query_location <= (int) strlen(querytext)); querytext += query_location; /* Length of 0 (or -1) means "rest of string" */ if (query_len <= 0) query_len = strlen(querytext); else Assert(query_len <= (int) strlen(querytext)); } else { /* If query location is unknown, distrust query_len as well */ query_location = 0; query_len = strlen(querytext); } /* * Discard leading and trailing whitespace, too. Use scanner_isspace() * not libc's isspace(), because we want to match the lexer's behavior. */ while (query_len > 0 && scanner_isspace(querytext[0])) querytext++, query_location++, query_len--; while (query_len > 0 && scanner_isspace(querytext[query_len - 1])) query_len--; #else query_len = (int) strlen(querytext); #endif minlen = Min(query_len,pgstat_track_activity_query_size-1); memcpy(ProcEntryArray[i].query,querytext,minlen); ProcEntryArray[i].query[minlen]='\0'; switch (query->commandType) { case CMD_SELECT: ProcEntryArray[i].cmdtype="SELECT"; break; case CMD_INSERT: ProcEntryArray[i].cmdtype="INSERT"; break; #if PG_VERSION_NUM >= 150000 case CMD_MERGE: ProcEntryArray[i].cmdtype="MERGE"; break; #endif case CMD_UPDATE: ProcEntryArray[i].cmdtype="UPDATE"; break; case CMD_DELETE: ProcEntryArray[i].cmdtype="DELETE"; break; case CMD_UTILITY: ProcEntryArray[i].cmdtype="UTILITY"; break; case CMD_UNKNOWN: ProcEntryArray[i].cmdtype="UNKNOWN"; break; case CMD_NOTHING: ProcEntryArray[i].cmdtype="NOTHING"; break; } /* * For utility statements, we just hash the query string to get an ID. */ #if PG_VERSION_NUM >= 110000 if (query->queryId == UINT64CONST(0)) { ProcEntryArray[i].queryid = getparsedinfo_hash64_string(querytext, query_len); #else if (query->queryId == 0) { ProcEntryArray[i].queryid = getparsedinfo_hash32_string(querytext, query_len); #endif } else { ProcEntryArray[i].queryid = query->queryId; } } } Datum get_parsedinfo(PG_FUNCTION_ARGS) { uint32 i; /*procEntry *result;*/ ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; TupleDesc tupdesc; Tuplestorestate *tupstore; MemoryContext per_query_ctx; MemoryContext oldcontext; Datum values[4]; bool nulls[4] = {0}; per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; oldcontext = MemoryContextSwitchTo(per_query_ctx); /* Build a tuple descriptor for our result type */ if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) elog(ERROR, "return type must be a row type"); tupstore = tuplestore_begin_heap(true, false, work_mem); rsinfo->returnMode = SFRM_Materialize; rsinfo->setResult = tupstore; rsinfo->setDesc = tupdesc; MemoryContextSwitchTo(oldcontext); for (i = 0; i < ProcGlobal->allProcCount; i++) { PGPROC *proc = &ProcGlobal->allProcs[i]; if (proc != NULL && proc->pid != 0 && (proc->pid == PG_GETARG_INT32(0) || PG_GETARG_INT32(0) == -1)) { values[0] = proc->pid; if (Int64GetDatum(ProcEntryArray[i].queryid)) values[1] = Int64GetDatum(ProcEntryArray[i].queryid); else nulls[1] = true; if (CStringGetTextDatum(ProcEntryArray[i].query)) values[2] = CStringGetTextDatum(ProcEntryArray[i].query); else nulls[2] = true; if (CStringGetTextDatum(ProcEntryArray[i].cmdtype)) values[3] = CStringGetTextDatum(ProcEntryArray[i].cmdtype); else nulls[3] = true; tuplestore_putvalues(tupstore, tupdesc, values, nulls); } } return (Datum) 0; } pgsentinel-1.3.1/src/pgsentinel--1.0--1.3.1.sql000066400000000000000000000002761512515062100204330ustar00rootroot00000000000000/* pgsentinel--1.0--1.3.1.sql */ -- complain if script is sourced in psql, rather than via ALTER EXTENSION \echo Use "ALTER EXTENSION pgsentinel UPDATE TO '1.3.1'" to load this file. \quit pgsentinel-1.3.1/src/pgsentinel--1.0.sql000066400000000000000000000047161512515062100177230ustar00rootroot00000000000000-- complain if script is sourced in psql, rather than via CREATE EXTENSION \echo Use "CREATE EXTENSION pgsentinel" to load this file. \quit CREATE FUNCTION pg_active_session_history( OUT ash_time timestamptz, OUT datid Oid, OUT datname text, OUT pid integer, OUT leader_pid integer, OUT usesysid Oid, OUT usename text, OUT application_name text, OUT client_addr text, OUT client_hostname text, OUT client_port integer, OUT backend_start timestamptz, OUT xact_start timestamptz, OUT query_start timestamptz, OUT state_change timestamptz, OUT wait_event_type text, OUT wait_event text, OUT state text, OUT backend_xid xid, OUT backend_xmin xid, OUT top_level_query text, OUT query text, OUT cmdtype text, OUT queryid bigint, OUT backend_type text, OUT blockers integer, OUT blockerpid integer, OUT blocker_state text ) RETURNS SETOF record AS 'MODULE_PATHNAME', 'pg_active_session_history' LANGUAGE C STRICT VOLATILE PARALLEL SAFE; -- Register a view on the function for ease of use. CREATE VIEW pg_active_session_history AS SELECT * FROM pg_active_session_history(); GRANT SELECT ON pg_active_session_history TO PUBLIC; CREATE FUNCTION pg_stat_statements_history( OUT ash_time timestamptz, OUT userid Oid, OUT dbid Oid, OUT queryid bigint, OUT calls bigint, OUT total_exec_time double precision, OUT rows bigint, OUT shared_blks_hit bigint, OUT shared_blks_read bigint, OUT shared_blks_dirtied bigint, OUT shared_blks_written bigint, OUT local_blks_hit bigint, OUT local_blks_read bigint, OUT local_blks_dirtied bigint, OUT local_blks_written bigint, OUT temp_blks_read bigint, OUT temp_blks_written bigint, OUT blk_read_time double precision, OUT blk_write_time double precision, OUT plans bigint, OUT total_plan_time double precision, OUT wal_records bigint, OUT wal_fpi bigint, OUT wal_bytes numeric ) RETURNS SETOF record AS 'MODULE_PATHNAME', 'pg_stat_statements_history' LANGUAGE C STRICT VOLATILE PARALLEL SAFE; -- Register a view on the function for ease of use. CREATE VIEW pg_stat_statements_history AS SELECT * FROM pg_stat_statements_history(); GRANT SELECT ON pg_stat_statements_history TO PUBLIC; CREATE FUNCTION get_parsedinfo(IN int, OUT pid integer, OUT queryid bigint, OUT query text,OUT cmdtype text) RETURNS SETOF RECORD AS 'MODULE_PATHNAME','get_parsedinfo' LANGUAGE C STRICT VOLATILE; pgsentinel-1.3.1/src/pgsentinel.c000066400000000000000000001713271512515062100170000ustar00rootroot00000000000000/* * pgsentinel.c * Track active session history. * * Copyright (c) 2018-2026, PgSentinel * * IDENTIFICATION: * https://github.com/pgsentinel/pgsentinel * * This program is open source, licensed under the PostgreSQL license. * For license terms, see the LICENSE file. */ #include "pgsentinel.h" #include "postgres.h" #include "fmgr.h" #include "access/xact.h" #include "lib/stringinfo.h" #include "pgstat.h" #include "executor/spi.h" #include "postmaster/bgworker.h" #include "storage/ipc.h" #include "storage/latch.h" #include "storage/proc.h" #include "utils/guc.h" #include "utils/snapmgr.h" #include "miscadmin.h" #include "storage/spin.h" #include "utils/date.h" #include "utils/builtins.h" #include "utils/memutils.h" #include "funcapi.h" #include "optimizer/planner.h" #include "storage/shm_toc.h" #include "access/twophase.h" #include "parser/analyze.h" #include "parser/scansup.h" #include "access/hash.h" #include "commands/extension.h" #include "catalog/namespace.h" PG_MODULE_MAGIC; PG_FUNCTION_INFO_V1(pg_active_session_history); PG_FUNCTION_INFO_V1(pg_stat_statements_history); #define PG_ACTIVE_SESSION_HISTORY_COLS 28 #define PG_STAT_STATEMENTS_HISTORY_COLS 24 #define EXTENSION_NAME "pgsentinel" /* Entry point of library loading */ void _PG_init(void); void _PG_fini(void); PGDLLEXPORT void pgsentinel_main(Datum); /* Signal handling */ static volatile sig_atomic_t got_sigterm = false; static volatile sig_atomic_t got_sighup = false; /* Saved hook values in case of unload */ static shmem_startup_hook_type ash_prev_shmem_startup_hook = NULL; #if (PG_VERSION_NUM >= 150000) static shmem_request_hook_type ash_prev_shmem_request_hook = NULL; #endif /* Our hooks */ static void ash_shmem_startup(void); static void ash_shmem_shutdown(int code, Datum arg); static void ash_shmem_request(void); /* GUC variables */ static int ash_sampling_period = 1; static int ash_max_entries = 1000; static int pgssh_max_entries = 10000; static bool pgssh_enable = false; static bool ash_track_idle_trans = false; static int ash_restart_wait_time = 2; static char *pgsentinelDbName = "postgres"; /* Worker name */ static char *worker_name = "pgsentinel"; /* pg_stat_activity query */ static const char * const pgsa_query_no_track_idle= #if (PG_VERSION_NUM / 100 ) == 906 "select act.datid, act.datname, act.pid, act.usesysid, act.usename, \ act.application_name, text(act.client_addr), act.client_hostname, \ act.client_port, act.backend_start, act.xact_start, act.query_start, \ act.state_change, case when act.wait_event_type is null then 'CPU' \ else act.wait_event_type end as wait_event_type,case when act.wait_event is null \ then 'CPU' else act.wait_event end as wait_event, act.state, act.backend_xid, \ act.backend_xmin, act.query,(pg_blocking_pids(act.pid))[1], \ cardinality(pg_blocking_pids(act.pid)),blk.state,gpi.* \ from pg_stat_activity act left join pg_stat_activity blk \ on (pg_blocking_pids(act.pid))[1] = blk.pid,get_parsedinfo(act.pid) gpi \ where act.state ='active' and act.pid != pg_backend_pid()"; #elif PG_VERSION_NUM < 130000 "select act.datid, act.datname, act.pid, act.usesysid, act.usename, \ act.application_name, text(act.client_addr), act.client_hostname, \ act.client_port, act.backend_start, act.xact_start, act.query_start, \ act.state_change, case when act.wait_event_type is null then 'CPU' \ else act.wait_event_type end as wait_event_type,case when act.wait_event is null \ then 'CPU' else act.wait_event end as wait_event, act.state, act.backend_xid, \ act.backend_xmin, act.query, act.backend_type,(pg_blocking_pids(act.pid))[1], \ cardinality(pg_blocking_pids(act.pid)),blk.state,gpi.* \ from pg_stat_activity act left join pg_stat_activity blk \ on (pg_blocking_pids(act.pid))[1] = blk.pid,get_parsedinfo(act.pid) gpi \ where act.state ='active' and act.pid != pg_backend_pid()"; #elif PG_VERSION_NUM < 160000 "select act.datid, act.datname, act.pid, act.usesysid, act.usename, \ act.application_name, text(act.client_addr), act.client_hostname, \ act.client_port, act.backend_start, act.xact_start, act.query_start, \ act.state_change, case when act.wait_event_type is null then 'CPU' \ else act.wait_event_type end as wait_event_type,case when act.wait_event is null \ then 'CPU' else act.wait_event end as wait_event, act.state, act.backend_xid, \ act.backend_xmin, act.query, act.backend_type,(pg_blocking_pids(act.pid))[1], \ cardinality(pg_blocking_pids(act.pid)),blk.state,gpi.*, act.leader_pid \ from pg_stat_activity act left join pg_stat_activity blk \ on (pg_blocking_pids(act.pid))[1] = blk.pid,get_parsedinfo(act.pid) gpi \ where act.state ='active' and act.pid != pg_backend_pid()"; #else "select act.datid, act.datname, act.pid, act.usesysid, act.usename, \ act.application_name, text(act.client_addr), act.client_hostname, \ act.client_port, act.backend_start, act.xact_start, act.query_start, \ act.state_change, case when act.wait_event_type is null then 'CPU' \ else act.wait_event_type end as wait_event_type,case when act.wait_event is null \ then 'CPU' else act.wait_event end as wait_event, act.state, act.backend_xid, \ act.backend_xmin, act.query, act.backend_type,(pg_blocking_pids(act.pid))[1], \ cardinality(pg_blocking_pids(act.pid)),blk.state,gpi.pid,act.query_id, \ gpi.query, gpi.cmdtype, act.leader_pid \ from pg_stat_activity act left join pg_stat_activity blk \ on (pg_blocking_pids(act.pid))[1] = blk.pid,get_parsedinfo(act.pid) gpi \ where act.state ='active' and act.pid != pg_backend_pid()"; #endif static const char * const pgsa_query_track_idle= #if (PG_VERSION_NUM / 100 ) == 906 "select act.datid, act.datname, act.pid, act.usesysid, act.usename, \ act.application_name, text(act.client_addr), act.client_hostname, \ act.client_port, act.backend_start, act.xact_start, act.query_start, \ act.state_change, case when act.wait_event_type is null then 'CPU' \ else act.wait_event_type end as wait_event_type,case when act.wait_event is null \ then 'CPU' else act.wait_event end as wait_event, act.state, act.backend_xid, \ act.backend_xmin, act.query,(pg_blocking_pids(act.pid))[1], \ cardinality(pg_blocking_pids(act.pid)),blk.state,gpi.* \ from pg_stat_activity act left join pg_stat_activity blk \ on (pg_blocking_pids(act.pid))[1] = blk.pid,get_parsedinfo(act.pid) gpi \ where act.state in ('active', 'idle in transaction') and act.pid != pg_backend_pid()"; #elif PG_VERSION_NUM < 130000 "select act.datid, act.datname, act.pid, act.usesysid, act.usename, \ act.application_name, text(act.client_addr), act.client_hostname, \ act.client_port, act.backend_start, act.xact_start, act.query_start, \ act.state_change, case when act.wait_event_type is null then 'CPU' \ else act.wait_event_type end as wait_event_type,case when act.wait_event is null \ then 'CPU' else act.wait_event end as wait_event, act.state, act.backend_xid, \ act.backend_xmin, act.query, act.backend_type,(pg_blocking_pids(act.pid))[1], \ cardinality(pg_blocking_pids(act.pid)),blk.state,gpi.* \ from pg_stat_activity act left join pg_stat_activity blk \ on (pg_blocking_pids(act.pid))[1] = blk.pid,get_parsedinfo(act.pid) gpi \ where act.state in ('active', 'idle in transaction') and act.pid != pg_backend_pid()"; #elif PG_VERSION_NUM < 160000 "select act.datid, act.datname, act.pid, act.usesysid, act.usename, \ act.application_name, text(act.client_addr), act.client_hostname, \ act.client_port, act.backend_start, act.xact_start, act.query_start, \ act.state_change, case when act.wait_event_type is null then 'CPU' \ else act.wait_event_type end as wait_event_type,case when act.wait_event is null \ then 'CPU' else act.wait_event end as wait_event, act.state, act.backend_xid, \ act.backend_xmin, act.query, act.backend_type,(pg_blocking_pids(act.pid))[1], \ cardinality(pg_blocking_pids(act.pid)),blk.state,gpi.*, act.leader_pid \ from pg_stat_activity act left join pg_stat_activity blk \ on (pg_blocking_pids(act.pid))[1] = blk.pid,get_parsedinfo(act.pid) gpi \ where act.state in ('active', 'idle in transaction') and act.pid != pg_backend_pid()"; #else "select act.datid, act.datname, act.pid, act.usesysid, act.usename, \ act.application_name, text(act.client_addr), act.client_hostname, \ act.client_port, act.backend_start, act.xact_start, act.query_start, \ act.state_change, case when act.wait_event_type is null then 'CPU' \ else act.wait_event_type end as wait_event_type,case when act.wait_event is null \ then 'CPU' else act.wait_event end as wait_event, act.state, act.backend_xid, \ act.backend_xmin, act.query, act.backend_type,(pg_blocking_pids(act.pid))[1], \ cardinality(pg_blocking_pids(act.pid)),blk.state,gpi.pid,act.query_id, \ gpi.query, gpi.cmdtype, act.leader_pid \ from pg_stat_activity act left join pg_stat_activity blk \ on (pg_blocking_pids(act.pid))[1] = blk.pid,get_parsedinfo(act.pid) gpi \ where act.state in ('active', 'idle in transaction') and act.pid != pg_backend_pid()"; #endif static const char * const pg_stat_statements_query= #if PG_VERSION_NUM < 130000 "select userid, dbid, queryid, calls, total_time, rows, shared_blks_hit, \ shared_blks_read, shared_blks_dirtied, shared_blks_written, local_blks_hit, \ local_blks_read, local_blks_dirtied, local_blks_written, temp_blks_read, \ temp_blks_written, blk_read_time, blk_write_time from pg_stat_statements \ where queryid in (select queryid from pg_active_session_history \ where ash_time in (select ash_time from pg_active_session_history \ order by ash_time desc limit 1))"; #else "select userid, dbid, queryid, calls, total_exec_time, rows, shared_blks_hit, \ shared_blks_read, shared_blks_dirtied, shared_blks_written, local_blks_hit, \ local_blks_read, local_blks_dirtied, local_blks_written, temp_blks_read, \ temp_blks_written, blk_read_time, blk_write_time, \ plans, total_plan_time, wal_records, wal_fpi, wal_bytes \ from pg_stat_statements \ where queryid in (select queryid from pg_active_session_history \ where ash_time in (select ash_time from pg_active_session_history \ order by ash_time desc limit 1))"; #endif static void pg_active_session_history_internal(FunctionCallInfo fcinfo); static void pg_stat_statements_history_internal(FunctionCallInfo fcinfo); procEntry *ProcEntryArray = NULL; post_parse_analyze_hook_type prev_post_parse_analyze_hook = NULL; /* ash entry */ typedef struct ashEntry { int pid; #if PG_VERSION_NUM >= 130000 int leader_pid; #endif int client_port; uint64 queryid; TimestampTz ash_time; Oid datid; Oid usesysid; char *usename; char *datname; char *application_name; char *wait_event_type; char *wait_event; char *state; char *blocker_state; char *client_hostname; int blockers; int blockerpid; char *top_level_query; char *query; char *cmdtype; char *backend_type; char *client_addr; TransactionId backend_xmin; TransactionId backend_xid; TimestampTz backend_start; TimestampTz xact_start; TimestampTz query_start; TimestampTz state_change; } ashEntry; /* pg_stat_statement_history entry */ typedef struct pgsshEntry { TimestampTz ash_time; Oid userid; Oid dbid; uint64 queryid; int64 calls; double total_time; int64 rows; int64 shared_blks_hit; int64 shared_blks_read; int64 shared_blks_dirtied; int64 shared_blks_written; int64 local_blks_hit; int64 local_blks_read; int64 local_blks_dirtied; int64 local_blks_written; int64 temp_blks_read; int64 temp_blks_written; double blk_read_time; double blk_write_time; #if PG_VERSION_NUM >= 130000 int64 plans; double total_plan_time; int64 wal_records; int64 wal_fpi; uint64 wal_bytes; #endif } pgsshEntry; /* counters */ typedef struct intEntry { int inserted; int pgsshinserted; } intEntry; /* For shared memory */ static char *AshEntryUsenameBuffer = NULL; static char *AshEntryDatnameBuffer = NULL; static char *AshEntryAppnameBuffer = NULL; static char *AshEntryWaitEventTypeBuffer = NULL; static char *AshEntryWaitEventBuffer = NULL; static char *AshEntryClientHostnameBuffer = NULL; static char *AshEntryTopLevelQueryBuffer = NULL; static char *AshEntryQueryBuffer = NULL; static char *AshEntryCmdTypeBuffer = NULL; static char *AshEntryBackendTypeBuffer = NULL; static char *AshEntryStateBuffer = NULL; static char *AshEntryBlockerStateBuffer = NULL; static char *AshEntryClientaddrBuffer = NULL; static ashEntry *AshEntryArray = NULL; static intEntry *IntEntryArray = NULL; static pgsshEntry *PgsshEntryArray = NULL; static char *ProcQueryBuffer = NULL; static char *ProcCmdTypeBuffer = NULL; /* Estimate amount of shared memory needed */ static Size ash_entry_memsize(void); /* check extension is loaded/present */ static bool PgSentinelHasBeenLoaded(void); /* store ash entry */ static void ash_entry_store(TimestampTz ash_time,const int pid, #if PG_VERSION_NUM >= 130000 int leader_pid, #endif const char *usename, const int client_port,Oid datid, const char *datname, const char *application_name, const char *client_addr, TransactionId backend_xmin, TimestampTz backend_start, TimestampTz xact_start, TimestampTz query_start, TimestampTz state_change, const char *wait_event_type, const char *wait_event, const char *state, const char *client_hostname, const char *query, const char *backend_type, Oid usesysid, TransactionId backend_xid, int blockers, int blockerpid, const char *blocker_state, uint64 queryid, const char *gpi_query, const char *cmdtype); /* prepare store ash */ static void ash_prepare_store(TimestampTz ash_time,const int pid, #if PG_VERSION_NUM >= 130000 int leader_pid, #endif const char* usename,const int client_port, Oid datid, const char *datname, const char *application_name, const char *client_addr, TransactionId backend_xmin, TimestampTz backend_start, TimestampTz xact_start, TimestampTz query_start, TimestampTz state_change, const char *wait_event_type, const char *wait_event, const char *state, const char *client_hostname, const char *query, const char *backend_type, Oid usesysid, TransactionId backend_xid, int blockers, int blockerpid, const char *blocker_state, uint64 queryid, const char *gpi_query, const char *cmdtype); /* Estimate amount of shared memory needed for ash entry */ static Size ash_entry_memsize(void) { Size size; /* AshEntryArray */ size = mul_size(sizeof(ashEntry), ash_max_entries); /* AshEntryUsenameBuffer */ size = add_size(size, mul_size(NAMEDATALEN, ash_max_entries)); /* AshEntryDatnameBuffer */ size = add_size(size, mul_size(NAMEDATALEN, ash_max_entries)); /* AshEntryAppnameBuffer */ size = add_size(size, mul_size(NAMEDATALEN, ash_max_entries)); /* AshEntryClientaddrBuffer */ size = add_size(size, mul_size(NAMEDATALEN, ash_max_entries)); /* AshEntryWaitEventTypeBuffer */ size = add_size(size, mul_size(NAMEDATALEN, ash_max_entries)); /* AshEntryWaitEventBuffer */ size = add_size(size, mul_size(NAMEDATALEN, ash_max_entries)); /* AshEntryStateBuffer */ size = add_size(size, mul_size(NAMEDATALEN, ash_max_entries)); /* AshEntryClientHostnameBuffer */ size = add_size(size, mul_size(NAMEDATALEN, ash_max_entries)); /* AshEntryQueryBuffer */ size = add_size(size, mul_size(pgstat_track_activity_query_size, ash_max_entries)); /* AshEntryCmdTypeBuffer */ size = add_size(size, mul_size(NAMEDATALEN, ash_max_entries)); /* AshEntryTopLevelQueryBuffer */ size = add_size(size, mul_size(pgstat_track_activity_query_size, ash_max_entries)); /* AshEntryBackendTypeBuffer */ size = add_size(size, mul_size(NAMEDATALEN, ash_max_entries)); /* AshEntryBlockerStateBuffer */ size = add_size(size, mul_size(NAMEDATALEN, ash_max_entries)); return size; } /* Estimate amount of shared memory needed for int entry*/ static Size int_entry_memsize(void) { Size size; /* IntEntryArray */ size = mul_size(sizeof(intEntry), 1); return size; } /* Estimate amount of shared memory needed for pgssh entry*/ static Size pgssh_entry_memsize(void) { Size size; /* PgsshEntryArray */ size = mul_size(sizeof(pgsshEntry), pgssh_max_entries); return size; } static void ash_shmem_startup(void) { Size size; bool found; char *buffer; int i; if (ash_prev_shmem_startup_hook) ash_prev_shmem_startup_hook(); size = mul_size(sizeof(ashEntry), ash_max_entries); AshEntryArray = (ashEntry *) ShmemInitStruct("Ash Entry Array", size, &found); if (!found) MemSet(AshEntryArray, 0, size); size = mul_size(sizeof(intEntry), 1); IntEntryArray = (intEntry *) ShmemInitStruct("int Entry Array", size, &found); if (!found) { MemSet(IntEntryArray, 0, size); IntEntryArray[0].inserted=0; IntEntryArray[0].pgsshinserted=0; } if (pgssh_enable) { size = mul_size(sizeof(pgsshEntry), pgssh_max_entries); PgsshEntryArray = (pgsshEntry *) ShmemInitStruct("pgssh Entry Array", size, &found); if (!found) MemSet(PgsshEntryArray, 0, size); } size = mul_size(sizeof(procEntry), get_max_procs_count()); ProcEntryArray = (procEntry *) ShmemInitStruct("Get_parsedinfo Proc Entry", size, &found); if (!found) { MemSet(ProcEntryArray, 0, size); } size = mul_size(pgstat_track_activity_query_size, get_max_procs_count()); ProcQueryBuffer = (char *) ShmemInitStruct("Proc Query Buffer", size, &found); if (!found) { MemSet(ProcQueryBuffer, 0, size); /* Initialize pointers. */ buffer = ProcQueryBuffer; for (i = 0; i < get_max_procs_count(); i++) { ProcEntryArray[i].query= buffer; buffer += pgstat_track_activity_query_size; } } size = mul_size(NAMEDATALEN, get_max_procs_count()); ProcCmdTypeBuffer = (char *) ShmemInitStruct("Proc CmdType Buffer", size, &found); if (!found) { MemSet(ProcCmdTypeBuffer, 0, size); /* Initialize pointers. */ buffer = ProcCmdTypeBuffer; for (i = 0; i < get_max_procs_count(); i++) { ProcEntryArray[i].cmdtype= buffer; buffer += NAMEDATALEN; } } size = mul_size(NAMEDATALEN, ash_max_entries); AshEntryUsenameBuffer = (char *) ShmemInitStruct("Ash Entry useName Buffer", size, &found); if (!found) { MemSet(AshEntryUsenameBuffer, 0, size); /* Initialize usename pointers. */ buffer = AshEntryUsenameBuffer; for (i = 0; i < ash_max_entries; i++) { AshEntryArray[i].usename = buffer; buffer += NAMEDATALEN; } } size = mul_size(NAMEDATALEN, ash_max_entries); AshEntryDatnameBuffer = (char *) ShmemInitStruct("Ash Entry DatName Buffer", size, &found); if (!found) { MemSet(AshEntryDatnameBuffer, 0, size); /* Initialize Datname pointers. */ buffer = AshEntryDatnameBuffer; for (i = 0; i < ash_max_entries; i++) { AshEntryArray[i].datname = buffer; buffer += NAMEDATALEN; } } size = mul_size(NAMEDATALEN, ash_max_entries); AshEntryAppnameBuffer = (char *) ShmemInitStruct("Ash Entry AppName Buffer", size, &found); if (!found) { MemSet(AshEntryAppnameBuffer, 0, size); /* Initialize Appname pointers. */ buffer = AshEntryAppnameBuffer; for (i = 0; i < ash_max_entries; i++) { AshEntryArray[i].application_name = buffer; buffer += NAMEDATALEN; } } size = mul_size(NAMEDATALEN, ash_max_entries); AshEntryClientaddrBuffer = (char *) ShmemInitStruct("Ash Entry Client Addr Buffer", size, &found); if (!found) { MemSet(AshEntryClientaddrBuffer, 0, size); /* Initialize Client Addr pointers. */ buffer = AshEntryClientaddrBuffer; for (i = 0; i < ash_max_entries; i++) { AshEntryArray[i].client_addr = buffer; buffer += NAMEDATALEN; } } size = mul_size(NAMEDATALEN, ash_max_entries); AshEntryWaitEventTypeBuffer = (char *) ShmemInitStruct("Ash Entry Wait event type Buffer", size, &found); if (!found) { MemSet(AshEntryWaitEventTypeBuffer, 0, size); /* Initialize wait event type pointers. */ buffer = AshEntryWaitEventTypeBuffer; for (i = 0; i < ash_max_entries; i++) { AshEntryArray[i].wait_event_type = buffer; buffer += NAMEDATALEN; } } size = mul_size(NAMEDATALEN, ash_max_entries); AshEntryWaitEventBuffer = (char *) ShmemInitStruct("Ash Entry Wait Event Buffer", size, &found); if (!found) { MemSet(AshEntryWaitEventBuffer, 0, size); /* Initialize wait event pointers. */ buffer = AshEntryWaitEventBuffer; for (i = 0; i < ash_max_entries; i++) { AshEntryArray[i].wait_event = buffer; buffer += NAMEDATALEN; } } size = mul_size(NAMEDATALEN, ash_max_entries); AshEntryStateBuffer = (char *) ShmemInitStruct("Ash Entry State Buffer", size, &found); if (!found) { MemSet(AshEntryStateBuffer, 0, size); /* Initialize state pointers. */ buffer = AshEntryStateBuffer; for (i = 0; i < ash_max_entries; i++) { AshEntryArray[i].state = buffer; buffer += NAMEDATALEN; } } size = mul_size(NAMEDATALEN, ash_max_entries); AshEntryClientHostnameBuffer = (char *) ShmemInitStruct("Ash Entry Client Hostname Buffer", size, &found); if (!found) { MemSet(AshEntryClientHostnameBuffer, 0, size); /* Initialize client hostname pointers. */ buffer = AshEntryClientHostnameBuffer; for (i = 0; i < ash_max_entries; i++) { AshEntryArray[i].client_hostname = buffer; buffer += NAMEDATALEN; } } size = mul_size(pgstat_track_activity_query_size, ash_max_entries); AshEntryTopLevelQueryBuffer = (char *) ShmemInitStruct("Ash Entry Top Level Query Buffer", size, &found); if (!found) { MemSet(AshEntryTopLevelQueryBuffer, 0, size); /* Initialize top level query pointers. */ buffer = AshEntryTopLevelQueryBuffer; for (i = 0; i < ash_max_entries; i++) { AshEntryArray[i].top_level_query = buffer; buffer += pgstat_track_activity_query_size; } } size = mul_size(pgstat_track_activity_query_size, ash_max_entries); AshEntryQueryBuffer = (char *) ShmemInitStruct("Ash Entry Query Buffer", size, &found); if (!found) { MemSet(AshEntryQueryBuffer, 0, size); /* Initialize query pointers. */ buffer = AshEntryQueryBuffer; for (i = 0; i < ash_max_entries; i++) { AshEntryArray[i].query = buffer; buffer += pgstat_track_activity_query_size; } } size = mul_size(NAMEDATALEN, ash_max_entries); AshEntryCmdTypeBuffer = (char *) ShmemInitStruct("Ash Entry CmdType Buffer", size, &found); if (!found) { MemSet(AshEntryCmdTypeBuffer, 0, size); /* Initialize cmdtype pointers. */ buffer = AshEntryCmdTypeBuffer; for (i = 0; i < ash_max_entries; i++) { AshEntryArray[i].cmdtype = buffer; buffer += NAMEDATALEN; } } size = mul_size(NAMEDATALEN, ash_max_entries); AshEntryBackendTypeBuffer = (char *) ShmemInitStruct("Ash Entry Backend Type Buffer", size, &found); if (!found) { MemSet(AshEntryBackendTypeBuffer, 0, size); /* Initialize backend type pointers. */ buffer = AshEntryBackendTypeBuffer; for (i = 0; i < ash_max_entries; i++) { AshEntryArray[i].backend_type = buffer; buffer += NAMEDATALEN; } } size = mul_size(NAMEDATALEN, ash_max_entries); AshEntryBlockerStateBuffer = (char *) ShmemInitStruct("Ash Entry Blocker State Buffer", size, &found); if (!found) { MemSet(AshEntryBlockerStateBuffer, 0, size); /* Initialize state pointers. */ buffer = AshEntryBlockerStateBuffer; for (i = 0; i < ash_max_entries; i++) { AshEntryArray[i].blocker_state = buffer; buffer += NAMEDATALEN; } } /* * set up a shmem exit hook to do whatever useful (dump to disk later on?). */ if (!IsUnderPostmaster) on_shmem_exit(ash_shmem_shutdown, (Datum) 0); if (found) return; } static void ash_shmem_shutdown(int code, Datum arg) { /* Don't try to dump during a crash. */ if (code) return; /* Safety check ... shouldn't get here unless shmem is set up. */ if (!AshEntryArray) return; /* dump to disk ?*/ /* This is the place */ } static void pgsentinel_sigterm(SIGNAL_ARGS) { int save_errno = errno; got_sigterm = true; #if PG_VERSION_NUM >= 100000 SetLatch(MyLatch); #else if (MyProc) SetLatch(&MyProc->procLatch); #endif errno = save_errno; } static void pgsentinel_sighup(SIGNAL_ARGS) { int save_errno = errno; got_sighup = true; #if PG_VERSION_NUM >= 100000 SetLatch(MyLatch); #else if (MyProc) SetLatch(&MyProc->procLatch); #endif errno = save_errno; } static void ash_entry_store(TimestampTz ash_time, const int pid, #if PG_VERSION_NUM >= 130000 int leader_pid, #endif const char *usename, const int client_port, Oid datid, const char *datname, const char *application_name, const char *client_addr, TransactionId backend_xmin, TimestampTz backend_start, TimestampTz xact_start, TimestampTz query_start, TimestampTz state_change, const char *wait_event_type, const char *wait_event, const char *state, const char *client_hostname, const char *query, const char *backend_type, Oid usesysid, TransactionId backend_xid, int blockers, int blockerpid, const char *blocker_state, uint64 queryid, const char *gpi_query, const char *cmdtype) { int inserted; inserted=IntEntryArray[0].inserted-1; memcpy(AshEntryArray[inserted].usename,usename,Min(strlen(usename)+1, NAMEDATALEN-1)); memcpy(AshEntryArray[inserted].datname,datname,Min(strlen(datname)+1, NAMEDATALEN-1)); memcpy(AshEntryArray[inserted].application_name,application_name, Min(strlen(application_name)+1,NAMEDATALEN-1)); memcpy(AshEntryArray[inserted].wait_event_type,wait_event_type, Min(strlen(wait_event_type)+1,NAMEDATALEN-1)); memcpy(AshEntryArray[inserted].wait_event,wait_event, Min(strlen(wait_event)+1,NAMEDATALEN-1)); memcpy(AshEntryArray[inserted].state,state,Min(strlen(state)+1, NAMEDATALEN-1)); memcpy(AshEntryArray[inserted].blocker_state,blocker_state, Min(strlen(blocker_state)+1,NAMEDATALEN-1)); memcpy(AshEntryArray[inserted].client_hostname,client_hostname, Min(strlen(client_hostname)+1,NAMEDATALEN-1)); memcpy(AshEntryArray[inserted].top_level_query,query, Min((int) strlen(query)+1,pgstat_track_activity_query_size-1)); memcpy(AshEntryArray[inserted].backend_type,backend_type, Min(strlen(backend_type)+1,NAMEDATALEN-1)); memcpy(AshEntryArray[inserted].client_addr,client_addr, Min(strlen(client_addr)+1,NAMEDATALEN-1)); memcpy(AshEntryArray[inserted].query,gpi_query,Min((int) strlen(gpi_query)+1, pgstat_track_activity_query_size-1)); memcpy(AshEntryArray[inserted].cmdtype,cmdtype,Min((int) strlen(cmdtype)+1, NAMEDATALEN-1)); AshEntryArray[inserted].client_port=client_port; AshEntryArray[inserted].datid=datid; AshEntryArray[inserted].usesysid=usesysid; AshEntryArray[inserted].pid=pid; #if PG_VERSION_NUM >= 130000 AshEntryArray[inserted].leader_pid=leader_pid; #endif AshEntryArray[inserted].backend_xmin=backend_xmin; AshEntryArray[inserted].backend_xid=backend_xid; AshEntryArray[inserted].backend_start=backend_start; AshEntryArray[inserted].xact_start=xact_start; AshEntryArray[inserted].query_start=query_start; AshEntryArray[inserted].state_change=state_change; AshEntryArray[inserted].ash_time=ash_time; AshEntryArray[inserted].blockers=blockers; AshEntryArray[inserted].blockerpid=blockerpid; AshEntryArray[inserted].queryid=queryid; } static void ash_prepare_store(TimestampTz ash_time, const int pid, #if PG_VERSION_NUM >= 130000 int leader_pid, #endif const char* usename, const int client_port, Oid datid, const char *datname, const char *application_name, const char *client_addr, TransactionId backend_xmin, TimestampTz backend_start, TimestampTz xact_start, TimestampTz query_start, TimestampTz state_change, const char *wait_event_type, const char *wait_event, const char *state, const char *client_hostname, const char *query, const char *backend_type, Oid usesysid, TransactionId backend_xid, int blockers, int blockerpid, const char *blocker_state, uint64 queryid, const char *gpi_query, const char *cmdtype) { /* Safety check... */ if (!AshEntryArray) { return; } IntEntryArray[0].inserted=(IntEntryArray[0].inserted % ash_max_entries) + 1; ash_entry_store(ash_time, pid, #if PG_VERSION_NUM >= 130000 leader_pid, #endif usename, client_port, datid, datname, application_name, client_addr,backend_xmin, backend_start, xact_start, query_start, state_change, wait_event_type, wait_event, state, client_hostname, query, backend_type, usesysid, backend_xid, blockers, blockerpid, blocker_state, queryid, gpi_query, cmdtype); } void pgsentinel_main(Datum main_arg) { ereport(LOG, (errmsg("starting bgworker pgsentinel"))); /* Register functions for SIGTERM/SIGHUP management */ pqsignal(SIGHUP, pgsentinel_sighup); pqsignal(SIGINT, SIG_IGN); pqsignal(SIGTERM, pgsentinel_sigterm); /* We're now ready to receive signals */ BackgroundWorkerUnblockSignals(); /* Connect to a database */ #if (PG_VERSION_NUM < 110000) BackgroundWorkerInitializeConnection(pgsentinelDbName, NULL); #else BackgroundWorkerInitializeConnection(pgsentinelDbName, NULL, 0); #endif while (!got_sigterm) { int rc, ret; uint64 i; bool gotactives; TimestampTz ash_time; MemoryContext uppercxt; gotactives=false; letswait: /* Wait necessary amount of time */ #if PG_VERSION_NUM >= 100000 rc = WaitLatch(MyLatch, WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH, ash_sampling_period * 1000L,PG_WAIT_EXTENSION); ResetLatch(MyLatch); #else rc = WaitLatch(&MyProc->procLatch, WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH, ash_sampling_period * 1000L); ResetLatch(&MyProc->procLatch); #endif CHECK_FOR_INTERRUPTS(); /* Emergency bailout if postmaster has died */ if (rc & WL_POSTMASTER_DEATH) proc_exit(1); /* Process signals */ if (got_sighup) { /* Process config file */ got_sighup = false; ProcessConfigFile(PGC_SIGHUP); ereport(LOG, (errmsg("bgworker pgsentinel signal: processed SIGHUP"))); } if (got_sigterm) { /* Simply exit */ ereport(LOG, (errmsg("bgworker pgsentinel signal: processed SIGTERM"))); proc_exit(0); } uppercxt = CurrentMemoryContext; SetCurrentStatementStartTimestamp(); StartTransactionCommand(); PushActiveSnapshot(GetTransactionSnapshot()); if (!PgSentinelHasBeenLoaded()) { PopActiveSnapshot(); CommitTransactionCommand(); goto letswait; } SPI_connect(); if (ash_track_idle_trans) { pgstat_report_activity(STATE_RUNNING, pgsa_query_track_idle); /* We can now execute queries via SPI */ ret = SPI_execute(pgsa_query_track_idle, true, 0); } else { pgstat_report_activity(STATE_RUNNING, pgsa_query_no_track_idle); /* We can now execute queries via SPI */ ret = SPI_execute(pgsa_query_no_track_idle, true, 0); } if (ret != SPI_OK_SELECT) elog(FATAL, "cannot select from pg_stat_activity: error code %d", ret); ash_time=GetCurrentTimestamp(); /* Do some processing */ if (SPI_processed > 0) { MemoryContext oldcxt = MemoryContextSwitchTo(uppercxt); gotactives=true; for (i = 0; i < SPI_processed; i++) { bool isnull; Datum data; char *usenamevalue=NULL; char *datnamevalue=NULL; char *appnamevalue=NULL; char *wait_event_typevalue=NULL; char *wait_eventvalue=NULL; char *client_hostnamevalue=NULL; char *queryvalue=NULL; char *backend_typevalue=NULL; char *statevalue=NULL; char *blockerstatevalue=NULL; char *clientaddrvalue=NULL; int pidvalue; #if PG_VERSION_NUM >= 130000 int leader_pidvalue; #endif int client_portvalue; int blockersvalue; int blockerpidvalue; Oid datidvalue; Oid usesysidvalue; TransactionId backend_xminvalue; TransactionId backend_xidvalue; TimestampTz backend_startvalue; TimestampTz xact_startvalue; TimestampTz query_startvalue; TimestampTz state_changevalue; uint64 queryidvalue; char *gpi_queryvalue = NULL; char *cmdtypevalue = NULL; /* Fetch values */ /* datid */ datidvalue = DatumGetObjectId(SPI_getbinval( SPI_tuptable->vals[i],SPI_tuptable->tupdesc,1, &isnull)); /* usesysid */ usesysidvalue = DatumGetObjectId(SPI_getbinval( SPI_tuptable->vals[i],SPI_tuptable->tupdesc,4, &isnull)); /* datname */ datnamevalue = DatumGetCString(SPI_getbinval( SPI_tuptable->vals[i],SPI_tuptable->tupdesc,2, &isnull)); /* pid */ pidvalue = DatumGetInt32(SPI_getbinval( SPI_tuptable->vals[i],SPI_tuptable->tupdesc,3, &isnull)); #if PG_VERSION_NUM >= 100000 /* blockerpid */ blockerpidvalue = DatumGetInt32(SPI_getbinval( SPI_tuptable->vals[i],SPI_tuptable->tupdesc,21, &isnull)); /* blockers */ blockersvalue = DatumGetInt32(SPI_getbinval( SPI_tuptable->vals[i],SPI_tuptable->tupdesc,22, &isnull)); #else /* blockerpid */ blockerpidvalue = DatumGetInt32(SPI_getbinval( SPI_tuptable->vals[i],SPI_tuptable->tupdesc,20, &isnull)); /* blockers */ blockersvalue = DatumGetInt32(SPI_getbinval( SPI_tuptable->vals[i],SPI_tuptable->tupdesc,21, &isnull)); #endif /* client_port */ client_portvalue = DatumGetInt32(SPI_getbinval( SPI_tuptable->vals[i],SPI_tuptable->tupdesc,9, &isnull)); /* usename */ data=SPI_getbinval(SPI_tuptable->vals[i],SPI_tuptable->tupdesc, 5, &isnull); if (!isnull) { usenamevalue = DatumGetCString(data); } /* appname */ data=SPI_getbinval(SPI_tuptable->vals[i],SPI_tuptable->tupdesc, 6, &isnull); if (!isnull) { appnamevalue = TextDatumGetCString(data); } /* wait_event_type */ data=SPI_getbinval(SPI_tuptable->vals[i],SPI_tuptable->tupdesc, 14, &isnull); if (!isnull) { wait_event_typevalue = TextDatumGetCString(data); } /* wait_event */ data=SPI_getbinval(SPI_tuptable->vals[i],SPI_tuptable->tupdesc, 15, &isnull); if (!isnull) { wait_eventvalue = TextDatumGetCString(data); } /* state */ data=SPI_getbinval(SPI_tuptable->vals[i],SPI_tuptable->tupdesc, 16, &isnull); if (!isnull) { statevalue = TextDatumGetCString(data); } #if PG_VERSION_NUM >= 100000 /* blocker state */ data=SPI_getbinval(SPI_tuptable->vals[i],SPI_tuptable->tupdesc, 23, &isnull); if (!isnull) { blockerstatevalue = TextDatumGetCString(data); } /* queryid */ queryidvalue = DatumGetUInt64(SPI_getbinval( SPI_tuptable->vals[i],SPI_tuptable->tupdesc,25, &isnull)); /* gpi query */ data=SPI_getbinval(SPI_tuptable->vals[i],SPI_tuptable->tupdesc, 26, &isnull); if (!isnull) { gpi_queryvalue = TextDatumGetCString(data); } /* cmdtype */ data=SPI_getbinval(SPI_tuptable->vals[i],SPI_tuptable->tupdesc, 27, &isnull); if (!isnull) { cmdtypevalue = TextDatumGetCString(data); } #else /* blocker state */ data=SPI_getbinval(SPI_tuptable->vals[i],SPI_tuptable->tupdesc, 22, &isnull); if (!isnull) { blockerstatevalue = TextDatumGetCString(data); } /* queryid */ queryidvalue = DatumGetUInt64(SPI_getbinval( SPI_tuptable->vals[i],SPI_tuptable->tupdesc,24, &isnull)); /* gpi query */ data=SPI_getbinval(SPI_tuptable->vals[i],SPI_tuptable->tupdesc, 25, &isnull); if (!isnull) { gpi_queryvalue = TextDatumGetCString(data); } /* cmdtype */ data=SPI_getbinval(SPI_tuptable->vals[i],SPI_tuptable->tupdesc, 26, &isnull); if (!isnull) { cmdtypevalue = TextDatumGetCString(data); } #endif /* client_hostname */ data=SPI_getbinval(SPI_tuptable->vals[i],SPI_tuptable->tupdesc, 8, &isnull); if (!isnull) { client_hostnamevalue = TextDatumGetCString(data); } /* query */ data=SPI_getbinval(SPI_tuptable->vals[i],SPI_tuptable->tupdesc, 19, &isnull); if (!isnull) { queryvalue = TextDatumGetCString(data); } #if PG_VERSION_NUM >= 100000 /* backend_type */ data=SPI_getbinval(SPI_tuptable->vals[i],SPI_tuptable->tupdesc, 20, &isnull); if (!isnull) { backend_typevalue = TextDatumGetCString(data); } #endif /* client addr */ data=SPI_getbinval(SPI_tuptable->vals[i],SPI_tuptable->tupdesc, 7, &isnull); if (!isnull) { clientaddrvalue = TextDatumGetCString(data); } /* backend xid */ backend_xidvalue = DatumGetTransactionId(SPI_getbinval( SPI_tuptable->vals[i],SPI_tuptable->tupdesc,17, &isnull)); /* backedn xmin */ backend_xminvalue = DatumGetTransactionId(SPI_getbinval( SPI_tuptable->vals[i],SPI_tuptable->tupdesc,18, &isnull)); /* backend start */ backend_startvalue = DatumGetTimestamp(SPI_getbinval( SPI_tuptable->vals[i],SPI_tuptable->tupdesc,10, &isnull)); /* xact start */ xact_startvalue = DatumGetTimestamp(SPI_getbinval( SPI_tuptable->vals[i],SPI_tuptable->tupdesc,11, &isnull)); /* query start */ query_startvalue = DatumGetTimestamp(SPI_getbinval( SPI_tuptable->vals[i],SPI_tuptable->tupdesc,12, &isnull)); /* state change */ state_changevalue = DatumGetTimestamp(SPI_getbinval( SPI_tuptable->vals[i],SPI_tuptable->tupdesc,13, &isnull)); #if PG_VERSION_NUM >= 130000 /* leader pid */ leader_pidvalue = DatumGetInt32(SPI_getbinval( SPI_tuptable->vals[i],SPI_tuptable->tupdesc,28, &isnull)); #endif /* prepare to store the entry */ ash_prepare_store(ash_time, pidvalue, #if PG_VERSION_NUM >= 130000 leader_pidvalue, #endif usenamevalue ? usenamevalue : "\0", client_portvalue, datidvalue, datnamevalue ? datnamevalue : "\0", appnamevalue ? appnamevalue : "\0", clientaddrvalue ? clientaddrvalue : "\0", backend_xminvalue, backend_startvalue, xact_startvalue,query_startvalue, state_changevalue, wait_event_typevalue ? wait_event_typevalue : "\0", wait_eventvalue ? wait_eventvalue : "\0", statevalue ? statevalue : "\0", client_hostnamevalue ? client_hostnamevalue : "\0", queryvalue ? queryvalue : "\0", backend_typevalue ? backend_typevalue : "\0", usesysidvalue, backend_xidvalue, blockersvalue, blockerpidvalue, blockerstatevalue ? blockerstatevalue : "\0", queryidvalue, gpi_queryvalue ? gpi_queryvalue : "\0", cmdtypevalue ? cmdtypevalue : "\0"); if (appnamevalue != NULL) { pfree(appnamevalue); } if (wait_event_typevalue != NULL) { pfree(wait_event_typevalue); } if (wait_eventvalue != NULL) { pfree(wait_eventvalue); } if (statevalue != NULL) { pfree(statevalue); } if (blockerstatevalue != NULL) { pfree(blockerstatevalue); } if (client_hostnamevalue != NULL) { pfree(client_hostnamevalue); } if (queryvalue != NULL) { pfree(queryvalue); } if (backend_typevalue != NULL) { pfree(backend_typevalue); } if (clientaddrvalue != NULL) { pfree(clientaddrvalue); } if (gpi_queryvalue != NULL) { pfree(gpi_queryvalue); } if (cmdtypevalue != NULL) { pfree(cmdtypevalue); } } MemoryContextSwitchTo(oldcxt); } SPI_finish(); PopActiveSnapshot(); CommitTransactionCommand(); pgstat_report_activity(STATE_IDLE, NULL); /* pg_stat_statement_history */ if (gotactives && pgssh_enable) { uppercxt = CurrentMemoryContext; SetCurrentStatementStartTimestamp(); StartTransactionCommand(); SPI_connect(); PushActiveSnapshot(GetTransactionSnapshot()); pgstat_report_activity(STATE_RUNNING, pg_stat_statements_query); /* We can now execute queries via SPI */ ret = SPI_execute(pg_stat_statements_query,true, 0); if (ret != SPI_OK_SELECT) elog(FATAL, "cannot select from pg_stat_statements: error code %d", ret); /* Do some processing */ if (SPI_processed > 0) { MemoryContext oldcxt = MemoryContextSwitchTo(uppercxt); for (i = 0; i < SPI_processed; i++) { bool isnull; IntEntryArray[0].pgsshinserted=(IntEntryArray[0].pgsshinserted % pgssh_max_entries) + 1; PgsshEntryArray[IntEntryArray[0].pgsshinserted-1].ash_time=ash_time; PgsshEntryArray[IntEntryArray[0].pgsshinserted-1].userid=DatumGetObjectId(SPI_getbinval(SPI_tuptable->vals[i],SPI_tuptable->tupdesc,1, &isnull)); PgsshEntryArray[IntEntryArray[0].pgsshinserted-1].dbid=DatumGetObjectId(SPI_getbinval(SPI_tuptable->vals[i],SPI_tuptable->tupdesc,2, &isnull)); PgsshEntryArray[IntEntryArray[0].pgsshinserted-1].queryid=DatumGetUInt64(SPI_getbinval(SPI_tuptable->vals[i],SPI_tuptable->tupdesc,3, &isnull)); PgsshEntryArray[IntEntryArray[0].pgsshinserted-1].calls=DatumGetInt64(SPI_getbinval(SPI_tuptable->vals[i],SPI_tuptable->tupdesc,4, &isnull)); PgsshEntryArray[IntEntryArray[0].pgsshinserted-1].total_time=DatumGetFloat8(SPI_getbinval(SPI_tuptable->vals[i],SPI_tuptable->tupdesc,5, &isnull)); PgsshEntryArray[IntEntryArray[0].pgsshinserted-1].rows=DatumGetInt64(SPI_getbinval(SPI_tuptable->vals[i],SPI_tuptable->tupdesc,6, &isnull)); PgsshEntryArray[IntEntryArray[0].pgsshinserted-1].shared_blks_hit=DatumGetInt64(SPI_getbinval(SPI_tuptable->vals[i],SPI_tuptable->tupdesc,7, &isnull)); PgsshEntryArray[IntEntryArray[0].pgsshinserted-1].shared_blks_read=DatumGetInt64(SPI_getbinval(SPI_tuptable->vals[i],SPI_tuptable->tupdesc,8, &isnull)); PgsshEntryArray[IntEntryArray[0].pgsshinserted-1].shared_blks_dirtied=DatumGetInt64(SPI_getbinval(SPI_tuptable->vals[i],SPI_tuptable->tupdesc,9, &isnull)); PgsshEntryArray[IntEntryArray[0].pgsshinserted-1].shared_blks_written=DatumGetInt64(SPI_getbinval(SPI_tuptable->vals[i],SPI_tuptable->tupdesc,10, &isnull)); PgsshEntryArray[IntEntryArray[0].pgsshinserted-1].local_blks_hit=DatumGetInt64(SPI_getbinval(SPI_tuptable->vals[i],SPI_tuptable->tupdesc,11, &isnull)); PgsshEntryArray[IntEntryArray[0].pgsshinserted-1].local_blks_read=DatumGetInt64(SPI_getbinval(SPI_tuptable->vals[i],SPI_tuptable->tupdesc,12, &isnull)); PgsshEntryArray[IntEntryArray[0].pgsshinserted-1].local_blks_dirtied=DatumGetInt64(SPI_getbinval(SPI_tuptable->vals[i],SPI_tuptable->tupdesc,13, &isnull)); PgsshEntryArray[IntEntryArray[0].pgsshinserted-1].local_blks_written=DatumGetInt64(SPI_getbinval(SPI_tuptable->vals[i],SPI_tuptable->tupdesc,14, &isnull)); PgsshEntryArray[IntEntryArray[0].pgsshinserted-1].temp_blks_read=DatumGetInt64(SPI_getbinval(SPI_tuptable->vals[i],SPI_tuptable->tupdesc,15, &isnull)); PgsshEntryArray[IntEntryArray[0].pgsshinserted-1].temp_blks_written=DatumGetInt64(SPI_getbinval(SPI_tuptable->vals[i],SPI_tuptable->tupdesc,16, &isnull)); PgsshEntryArray[IntEntryArray[0].pgsshinserted-1].blk_read_time=DatumGetFloat8(SPI_getbinval(SPI_tuptable->vals[i],SPI_tuptable->tupdesc,17, &isnull)); PgsshEntryArray[IntEntryArray[0].pgsshinserted-1].blk_write_time=DatumGetFloat8(SPI_getbinval(SPI_tuptable->vals[i],SPI_tuptable->tupdesc,18, &isnull)); #if PG_VERSION_NUM >= 130000 PgsshEntryArray[IntEntryArray[0].pgsshinserted-1].plans=DatumGetInt64(SPI_getbinval(SPI_tuptable->vals[i],SPI_tuptable->tupdesc,19, &isnull)); PgsshEntryArray[IntEntryArray[0].pgsshinserted-1].total_plan_time=DatumGetFloat8(SPI_getbinval(SPI_tuptable->vals[i],SPI_tuptable->tupdesc,20, &isnull)); PgsshEntryArray[IntEntryArray[0].pgsshinserted-1].wal_records=DatumGetInt64(SPI_getbinval(SPI_tuptable->vals[i],SPI_tuptable->tupdesc,21, &isnull)); PgsshEntryArray[IntEntryArray[0].pgsshinserted-1].wal_fpi=DatumGetInt64(SPI_getbinval(SPI_tuptable->vals[i],SPI_tuptable->tupdesc,22, &isnull)); PgsshEntryArray[IntEntryArray[0].pgsshinserted-1].wal_bytes=DatumGetUInt64(SPI_getbinval(SPI_tuptable->vals[i],SPI_tuptable->tupdesc,23, &isnull)); #endif } MemoryContextSwitchTo(oldcxt); } SPI_finish(); PopActiveSnapshot(); CommitTransactionCommand(); pgstat_report_activity(STATE_IDLE, NULL); } } /* No problems, so clean exit */ proc_exit(0); } static void pgsentinel_load_params(void) { DefineCustomIntVariable("pgsentinel_ash.sampling_period", "Duration between each pull (in seconds).", NULL, &ash_sampling_period, 1, 1, INT_MAX, PGC_SIGHUP, 0, NULL, NULL, NULL); DefineCustomBoolVariable("pgsentinel_ash.track_idle_trans", "Track session in idle transaction state.", NULL, &ash_track_idle_trans, false, PGC_SIGHUP, 0, NULL, NULL, NULL); if (!process_shared_preload_libraries_in_progress) return; /* can't define PGC_POSTMASTER variable after startup */ DefineCustomIntVariable("pgsentinel_ash.max_entries", "Maximum number of ash entries.", NULL, &ash_max_entries, 1000, 1000, INT_MAX, PGC_POSTMASTER, 0, NULL, NULL, NULL); EmitWarningsOnPlaceholders("pgsentinel_ash"); DefineCustomIntVariable("pgsentinel_pgssh.max_entries", "Maximum number of pgssh entries.", NULL, &pgssh_max_entries, 10000, 10000, INT_MAX, PGC_POSTMASTER, 0, NULL, NULL, NULL); DefineCustomBoolVariable("pgsentinel_pgssh.enable", "Enable pg_stat_statements_history.", NULL, &pgssh_enable, false, PGC_POSTMASTER, 0, NULL, NULL, NULL); EmitWarningsOnPlaceholders("pgsentinel_pgssh"); DefineCustomStringVariable("pgsentinel.db_name", gettext_noop("Database on which the worker connect."), NULL, &pgsentinelDbName, "postgres", PGC_POSTMASTER, GUC_SUPERUSER_ONLY, NULL, NULL, NULL); } /* * Entry point for worker loading */ void _PG_init(void) { BackgroundWorker worker; if (IsBinaryUpgrade) return; /* Add parameters */ pgsentinel_load_params(); if (!process_shared_preload_libraries_in_progress) return; #if PG_VERSION_NUM < 150000 ash_shmem_request(); #endif /* * Install hooks. */ #if PG_VERSION_NUM >= 150000 ash_prev_shmem_request_hook = shmem_request_hook; shmem_request_hook = ash_shmem_request; #endif ash_prev_shmem_startup_hook = shmem_startup_hook; shmem_startup_hook = ash_shmem_startup; prev_post_parse_analyze_hook = post_parse_analyze_hook; post_parse_analyze_hook = getparsedinfo_post_parse_analyze; /* Worker parameter and registration */ memset(&worker, 0, sizeof(worker)); worker.bgw_flags = BGWORKER_SHMEM_ACCESS | BGWORKER_BACKEND_DATABASE_CONNECTION; worker.bgw_start_time = BgWorkerStart_ConsistentState; #if PG_VERSION_NUM >= 100000 sprintf(worker.bgw_library_name, "pgsentinel"); sprintf(worker.bgw_function_name, "pgsentinel_main"); #else worker.bgw_main = pgsentinel_main; #endif snprintf(worker.bgw_name, BGW_MAXLEN, "%s", worker_name); /* Wait ash_restart_wait_time seconds for restart before crash */ worker.bgw_restart_time = ash_restart_wait_time; worker.bgw_main_arg = (Datum) 0; #if PG_VERSION_NUM >= 90400 /* * Notify PID is present since 9.4. If this is not initialized * a static background worker cannot start properly. */ worker.bgw_notify_pid = 0; #endif RegisterBackgroundWorker(&worker); } static void pg_active_session_history_internal(FunctionCallInfo fcinfo) { ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; TupleDesc tupdesc; Tuplestorestate *tupstore; MemoryContext per_query_ctx; MemoryContext oldcontext; int i; /* Entry array must exist already */ if (!AshEntryArray) ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("pg_active_session_history must be loaded via shared_preload_libraries"))); /* check to see if caller supports us returning a tuplestore */ if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("set-valued function called in context that cannot accept a set"))); if (!(rsinfo->allowedModes & SFRM_Materialize)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("materialize mode required, but it is not " \ "allowed in this context"))); /* Switch context to construct returned data structures */ per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; oldcontext = MemoryContextSwitchTo(per_query_ctx); /* Build a tuple descriptor */ if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) elog(ERROR, "return type must be a row type"); tupstore = tuplestore_begin_heap(true, false, work_mem); rsinfo->returnMode = SFRM_Materialize; rsinfo->setResult = tupstore; rsinfo->setDesc = tupdesc; MemoryContextSwitchTo(oldcontext); for (i = 0; i < ash_max_entries; i++) { Datum values[PG_ACTIVE_SESSION_HISTORY_COLS]; bool nulls[PG_ACTIVE_SESSION_HISTORY_COLS]; int j = 0; memset(values, 0, sizeof(values)); memset(nulls, 0, sizeof(nulls)); // ash_time if (TimestampTzGetDatum(AshEntryArray[i].ash_time)) values[j++] = TimestampTzGetDatum(AshEntryArray[i].ash_time); else break; // datid if (ObjectIdGetDatum(AshEntryArray[i].datid)) values[j++] = ObjectIdGetDatum(AshEntryArray[i].datid); else nulls[j++] = true; // datname if (AshEntryArray[i].datname[0] != '\0') values[j++] = CStringGetTextDatum(AshEntryArray[i].datname); else nulls[j++] = true; // pid if (Int32GetDatum(AshEntryArray[i].pid)) values[j++] = Int32GetDatum(AshEntryArray[i].pid); else nulls[j++] = true; #if PG_VERSION_NUM >= 130000 // leader_pid if (Int32GetDatum(AshEntryArray[i].leader_pid)) values[j++] = Int32GetDatum(AshEntryArray[i].leader_pid); else nulls[j++] = true; #else nulls[j++] = true; #endif // usesysid if (ObjectIdGetDatum(AshEntryArray[i].usesysid)) values[j++] = ObjectIdGetDatum(AshEntryArray[i].usesysid); else nulls[j++] = true; // usename if (AshEntryArray[i].usename[0] != '\0') values[j++] = CStringGetTextDatum(AshEntryArray[i].usename); else nulls[j++] = true; // application_name if (AshEntryArray[i].application_name[0] != '\0') values[j++] = CStringGetTextDatum(AshEntryArray[i].application_name); else nulls[j++] = true; // client_addr if (AshEntryArray[i].client_addr[0] != '\0') values[j++] = CStringGetTextDatum(AshEntryArray[i].client_addr); else nulls[j++] = true; // client_hostname if (AshEntryArray[i].client_hostname[0] != '\0') values[j++] = CStringGetTextDatum(AshEntryArray[i].client_hostname); else nulls[j++] = true; // client_port if (Int32GetDatum(AshEntryArray[i].client_port)) values[j++] = Int32GetDatum(AshEntryArray[i].client_port); else nulls[j++] = true; // backend_start if (TimestampTzGetDatum(AshEntryArray[i].backend_start)) values[j++] = TimestampTzGetDatum(AshEntryArray[i].backend_start); else nulls[j++] = true; // xact_start if (TimestampTzGetDatum(AshEntryArray[i].xact_start)) values[j++] = TimestampTzGetDatum(AshEntryArray[i].xact_start); else nulls[j++] = true; // query_start if (TimestampTzGetDatum(AshEntryArray[i].query_start)) values[j++] = TimestampTzGetDatum(AshEntryArray[i].query_start); else nulls[j++] = true; // state_change if (TimestampTzGetDatum(AshEntryArray[i].state_change)) values[j++] = TimestampTzGetDatum(AshEntryArray[i].state_change); else nulls[j++] = true; // wait_event_type if (AshEntryArray[i].wait_event_type[0] != '\0') values[j++] = CStringGetTextDatum(AshEntryArray[i].wait_event_type); else nulls[j++] = true; // wait_event if (AshEntryArray[i].wait_event[0] != '\0') values[j++] = CStringGetTextDatum(AshEntryArray[i].wait_event); else nulls[j++] = true; // state if (AshEntryArray[i].state[0] != '\0') values[j++] = CStringGetTextDatum(AshEntryArray[i].state); else nulls[j++] = true; // backend_xid if (TransactionIdGetDatum(AshEntryArray[i].backend_xid)) values[j++] = TransactionIdGetDatum(AshEntryArray[i].backend_xid); else nulls[j++] = true; // backend_xmin if (TransactionIdGetDatum(AshEntryArray[i].backend_xmin)) values[j++] = TransactionIdGetDatum(AshEntryArray[i].backend_xmin); else nulls[j++] = true; // top_level_query if (AshEntryArray[i].top_level_query[0] != '\0') values[j++] = CStringGetTextDatum(AshEntryArray[i].top_level_query); else nulls[j++] = true; // query if (AshEntryArray[i].query[0] != '\0') values[j++] = CStringGetTextDatum(AshEntryArray[i].query); else nulls[j++] = true; // cmdtype if (AshEntryArray[i].cmdtype[0] != '\0') values[j++] = CStringGetTextDatum(AshEntryArray[i].cmdtype); else nulls[j++] = true; // query_id if (AshEntryArray[i].queryid) values[j++] = Int64GetDatum(AshEntryArray[i].queryid); else nulls[j++] = true; // backend_type if (AshEntryArray[i].backend_type[0] != '\0') values[j++] = CStringGetTextDatum(AshEntryArray[i].backend_type); else nulls[j++] = true; // blockers if (Int32GetDatum(AshEntryArray[i].blockers)) values[j++] = Int32GetDatum(AshEntryArray[i].blockers); else nulls[j++] = true; // blockerspid if (Int32GetDatum(AshEntryArray[i].blockerpid)) values[j++] = Int32GetDatum(AshEntryArray[i].blockerpid); else nulls[j++] = true; // blocker state if (AshEntryArray[i].blocker_state[0] != '\0') values[j++] = CStringGetTextDatum(AshEntryArray[i].blocker_state); else nulls[j++] = true; tuplestore_putvalues(tupstore, tupdesc, values, nulls); } } static void pg_stat_statements_history_internal(FunctionCallInfo fcinfo) { ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; TupleDesc tupdesc; Tuplestorestate *tupstore; MemoryContext per_query_ctx; MemoryContext oldcontext; int i; if (!pgssh_enable) ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("pg_stat_statements_history not enabled, set pgsentinel_pgssh.enable"))); /* Entry array must exist already */ if (!PgsshEntryArray) ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("pg_stat_statements_history must be loaded via shared_preload_libraries"))); /* check to see if caller supports us returning a tuplestore */ if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("set-valued function called in context that cannot accept a set"))); if (!(rsinfo->allowedModes & SFRM_Materialize)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("materialize mode required, but it is not " \ "allowed in this context"))); /* Switch context to construct returned data structures */ per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; oldcontext = MemoryContextSwitchTo(per_query_ctx); /* Build a tuple descriptor */ if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) elog(ERROR, "return type must be a row type"); tupstore = tuplestore_begin_heap(true, false, work_mem); rsinfo->returnMode = SFRM_Materialize; rsinfo->setResult = tupstore; rsinfo->setDesc = tupdesc; MemoryContextSwitchTo(oldcontext); for (i = 0; i < pgssh_max_entries; i++) { Datum values[PG_STAT_STATEMENTS_HISTORY_COLS]; bool nulls[PG_STAT_STATEMENTS_HISTORY_COLS]; int j = 0; #if PG_VERSION_NUM >= 130000 char buf[256]; Datum wal_bytes; #endif memset(values, 0, sizeof(values)); memset(nulls, 0, sizeof(nulls)); // ash_time if (TimestampTzGetDatum(PgsshEntryArray[i].ash_time)) values[j++] = TimestampTzGetDatum(PgsshEntryArray[i].ash_time); else break; // userid if (ObjectIdGetDatum(PgsshEntryArray[i].userid)) values[j++] = ObjectIdGetDatum(PgsshEntryArray[i].userid); else nulls[j++] = true; // dbid if (ObjectIdGetDatum(PgsshEntryArray[i].dbid)) values[j++] = ObjectIdGetDatum(PgsshEntryArray[i].dbid); else nulls[j++] = true; // query_id if (Int64GetDatum(PgsshEntryArray[i].queryid)) values[j++] = Int64GetDatum(PgsshEntryArray[i].queryid); else nulls[j++] = true; // calls if (Int64GetDatum(PgsshEntryArray[i].calls)) values[j++] = Int64GetDatum(PgsshEntryArray[i].calls); else values[j++] = 0; // total_time if (Float8GetDatum(PgsshEntryArray[i].total_time)) values[j++] = Float8GetDatum(PgsshEntryArray[i].total_time); else values[j++] = 0; // rows if (Int64GetDatum(PgsshEntryArray[i].rows)) values[j++] = Int64GetDatum(PgsshEntryArray[i].rows); else values[j++] = 0; // shared_blks_hit if (Int64GetDatum(PgsshEntryArray[i].shared_blks_hit)) values[j++] = Int64GetDatum(PgsshEntryArray[i].shared_blks_hit); else values[j++] = 0; // shared_blks_read if (Int64GetDatum(PgsshEntryArray[i].shared_blks_read)) values[j++] = Int64GetDatum(PgsshEntryArray[i].shared_blks_read); else values[j++] = 0; // shared_blks_dirtied if (Int64GetDatum(PgsshEntryArray[i].shared_blks_dirtied)) values[j++] = Int64GetDatum(PgsshEntryArray[i].shared_blks_dirtied); else values[j++] = 0; // shared_blks_written if (Int64GetDatum(PgsshEntryArray[i].shared_blks_written)) values[j++] = Int64GetDatum(PgsshEntryArray[i].shared_blks_written); else values[j++] = 0; // local_blks_hit if (Int64GetDatum(PgsshEntryArray[i].local_blks_hit)) values[j++] = Int64GetDatum(PgsshEntryArray[i].local_blks_hit); else values[j++] = 0; // local_blks_read if (Int64GetDatum(PgsshEntryArray[i].local_blks_read)) values[j++] = Int64GetDatum(PgsshEntryArray[i].local_blks_read); else values[j++] = 0; // local_blks_dirtied if (Int64GetDatum(PgsshEntryArray[i].local_blks_dirtied)) values[j++] = Int64GetDatum(PgsshEntryArray[i].local_blks_dirtied); else values[j++] = 0; // local_blks_written if (Int64GetDatum(PgsshEntryArray[i].local_blks_written)) values[j++] = Int64GetDatum(PgsshEntryArray[i].local_blks_written); else values[j++] = 0; // temp_blks_read if (Int64GetDatum(PgsshEntryArray[i].temp_blks_read)) values[j++] = Int64GetDatum(PgsshEntryArray[i].temp_blks_read); else values[j++] = 0; // temp_blks_written if (Int64GetDatum(PgsshEntryArray[i].temp_blks_written)) values[j++] = Int64GetDatum(PgsshEntryArray[i].temp_blks_written); else values[j++] = 0; // blk_read_time if (Float8GetDatum(PgsshEntryArray[i].blk_read_time)) values[j++] = Float8GetDatum(PgsshEntryArray[i].blk_read_time); else values[j++] = 0; // blk_write_time if (Float8GetDatum(PgsshEntryArray[i].blk_write_time)) values[j++] = Float8GetDatum(PgsshEntryArray[i].blk_write_time); else values[j++] = 0; #if PG_VERSION_NUM >= 130000 // plans if (Int64GetDatum(PgsshEntryArray[i].plans)) values[j++] = Int64GetDatum(PgsshEntryArray[i].plans); else values[j++] = 0; // total_plan_time if (Float8GetDatum(PgsshEntryArray[i].total_plan_time)) values[j++] = Float8GetDatum(PgsshEntryArray[i].total_plan_time); else values[j++] = 0; // wal_records if (Int64GetDatum(PgsshEntryArray[i].wal_records)) values[j++] = Int64GetDatum(PgsshEntryArray[i].wal_records); else values[j++] = 0; // wal_fpi if (Int64GetDatum(PgsshEntryArray[i].wal_fpi)) values[j++] = Int64GetDatum(PgsshEntryArray[i].wal_fpi); else values[j++] = 0; // wal_bytes snprintf(buf, sizeof buf, UINT64_FORMAT, PgsshEntryArray[i].wal_bytes); /* Convert to numeric. */ wal_bytes = DirectFunctionCall3(numeric_in, CStringGetDatum(buf), ObjectIdGetDatum(0), Int32GetDatum(-1)); values[j++] = wal_bytes; #else nulls[j++] = true; nulls[j++] = true; nulls[j++] = true; nulls[j++] = true; nulls[j++] = true; #endif tuplestore_putvalues(tupstore, tupdesc, values, nulls); } } Datum pg_active_session_history(PG_FUNCTION_ARGS) { pg_active_session_history_internal(fcinfo); return (Datum) 0; } Datum pg_stat_statements_history(PG_FUNCTION_ARGS) { pg_stat_statements_history_internal(fcinfo); return (Datum) 0; } void _PG_fini(void) { /* Uninstall hooks. */ shmem_startup_hook = ash_prev_shmem_startup_hook; post_parse_analyze_hook = prev_post_parse_analyze_hook; } static bool PgSentinelHasBeenLoaded(void) { bool extensionLoaded = false; bool extensionPresent = false; bool extensionScriptExecuted = true; Oid extensionOid = get_extension_oid(EXTENSION_NAME, true); if (extensionOid != InvalidOid) extensionPresent = true; if (extensionPresent) { /* check if extension objects are still being created */ if (creating_extension && CurrentExtensionObject == extensionOid) extensionScriptExecuted = false; else if (IsBinaryUpgrade) extensionScriptExecuted = false; } extensionLoaded = extensionPresent && extensionScriptExecuted; return extensionLoaded; } /* * shmem_request hook: request additional shared resources. We'll allocate or * attach to the shared resources in ash_shmem_startup(). */ static void ash_shmem_request(void) { #if PG_VERSION_NUM >= 150000 if (ash_prev_shmem_request_hook) ash_prev_shmem_request_hook(); #endif RequestAddinShmemSpace(ash_entry_memsize()); RequestNamedLWLockTranche("Ash Entry Array", 1); RequestAddinShmemSpace(proc_entry_memsize()); RequestNamedLWLockTranche("Get_parsedinfo Proc Entry Array", 1); RequestAddinShmemSpace(int_entry_memsize()); RequestNamedLWLockTranche("Int Entry Array", 1); if (pgssh_enable) { RequestAddinShmemSpace(pgssh_entry_memsize()); RequestNamedLWLockTranche("Pgssh Entry Array", 1); } } pgsentinel-1.3.1/src/pgsentinel.conf000066400000000000000000000001451512515062100174700ustar00rootroot00000000000000shared_preload_libraries = 'pg_stat_statements,pgsentinel' pgsentinel.db_name = 'contrib_regression' pgsentinel-1.3.1/src/pgsentinel.control000066400000000000000000000001671512515062100202270ustar00rootroot00000000000000comment = 'active session history' default_version = '1.3.1' module_pathname = '$libdir/pgsentinel' relocatable = true pgsentinel-1.3.1/src/pgsentinel.h000066400000000000000000000015521512515062100167750ustar00rootroot00000000000000#ifndef __PG_SENTINEL_H__ #define __PG_SENTINEL_H__ #include #include "parser/analyze.h" /* Check PostgreSQL version */ #if PG_VERSION_NUM < 90600 #error "You are trying to build pg_sentinel with PostgreSQL version < 9.6" #endif /* Saved hook values in case of unload */ extern post_parse_analyze_hook_type prev_post_parse_analyze_hook; /* Our hooks */ #if PG_VERSION_NUM < 140000 extern void getparsedinfo_post_parse_analyze(ParseState *pstate, Query *query); #else extern void getparsedinfo_post_parse_analyze(ParseState *pstate, Query *query, JumbleState *jstate); #endif /* Estimate amount of shared memory needed */ extern Size proc_entry_memsize(void); extern int get_max_procs_count(void); typedef struct procEntry { uint64 queryid; char *query; char *cmdtype; } procEntry; extern procEntry *ProcEntryArray; #endif pgsentinel-1.3.1/src/sql/000077500000000000000000000000001512515062100152505ustar00rootroot00000000000000pgsentinel-1.3.1/src/sql/pgsentinel-test.sql000066400000000000000000000011561512515062100211210ustar00rootroot00000000000000CREATE EXTENSION pg_stat_statements; CREATE EXTENSION pgsentinel; select pg_sleep(3); select count(*) > 0 AS has_data from pg_active_session_history where queryid in (select queryid from pg_stat_statements); begin; \! sleep 3 commit; select count(*) > 0 AS has_idle_data from pg_active_session_history where state = 'idle in transaction'; ALTER SYSTEM SET pgsentinel_ash.track_idle_trans = true; select pg_reload_conf(); begin; \! sleep 3 commit; select count(*) > 0 AS has_idle_data from pg_active_session_history where state = 'idle in transaction'; DROP EXTENSION pgsentinel; DROP EXTENSION pg_stat_statements;