├── LICENSE ├── Makefile ├── README.md ├── README_REGTEST ├── agent ├── Makefile ├── bin │ ├── Makefile │ ├── archive_pglog.sh │ ├── autovacuum.c │ ├── checkpoint.c │ ├── collector.c │ ├── collector_sql.h │ ├── collector_wait_sampling.c │ ├── logger.c │ ├── logger_common.c │ ├── logger_in.c │ ├── logger_out.c │ ├── logger_send.c │ ├── maintenance.c │ ├── pg_control.c │ ├── pg_statsinfod.c │ ├── pg_statsinfod.h │ ├── pg_statsrepo.sql │ ├── pg_statsrepo_alert.sql │ ├── pgut │ │ ├── pgut-list.c │ │ ├── pgut-list.h │ │ ├── pgut-pthread.c │ │ ├── pgut-pthread.h │ │ ├── pgut.c │ │ └── pgut.h │ ├── snapshot.c │ ├── uninstall_pg_statsrepo.sql │ ├── writer.c │ └── writer_sql.h ├── common.h └── lib │ ├── Makefile │ ├── last_xact_activity.c │ ├── library.map │ ├── libstatsinfo.c │ ├── libstatsinfo.h │ ├── pg_control.c │ ├── pg_statsinfo.sql.in │ ├── pgut │ ├── pgut-be.h │ ├── pgut-spi.c │ └── pgut-spi.h │ ├── port.c │ ├── rusage.h │ ├── uninstall_pg_statsinfo.sql │ ├── wait_sampling.c │ └── wait_sampling.h ├── doc ├── files │ ├── pg_statsinfo_v17_report_infomation.xls │ └── pg_statsinfo_v17_repository_infomation.xls ├── image │ ├── pg_statsinfo-ja.png │ ├── pg_statsinfo.png │ ├── system_structure-ja.png │ └── system_structure.png ├── pg_statsinfo-ja.md └── pg_statsinfo.md ├── reporter ├── Makefile ├── control.c ├── pg_statsinfo.c ├── pg_statsinfo.h ├── pgut │ ├── pgut-fe.c │ ├── pgut-fe.h │ ├── pgut-list.c │ ├── pgut-list.h │ ├── pgut.c │ └── pgut.h ├── report.c └── snapshot.c ├── spec ├── README ├── pg_statsinfo-tmpfiles.d.conf └── pg_statsinfo.spec └── test ├── expected ├── function-alert.out ├── function-command_option.out ├── function-logger.out ├── function-logstore.out ├── function-maintenance.out ├── function-report.out ├── function-snapshot.out └── function-snapshot_replication.out ├── regress.sh └── script ├── common.sh ├── config ├── pg_hba-replication.conf ├── postgresql-alert.conf ├── postgresql-logger.conf ├── postgresql-logical-sby.conf ├── postgresql-logstore.conf ├── postgresql-maintenance.conf ├── postgresql-replication-act.conf ├── postgresql-replication-sby.conf ├── postgresql-report.conf ├── postgresql-repository.conf └── postgresql-snapshot.conf ├── function-alert.sh ├── function-command_option.sh ├── function-logger.sh ├── function-logstore.sh ├── function-maintenance.sh ├── function-report.sh ├── function-snapshot.sh ├── function-snapshot_replication.sh └── inputdata └── statsrepo-inputdata.sql /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009-2025, NIPPON TELEGRAPH AND TELEPHONE CORPORATION 2 | 3 | Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group 4 | Portions Copyright (c) 1994, The Regents of the University of California 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, 10 | this list of conditions and the following disclaimer. 11 | * Redistributions in binary form must reproduce the above copyright 12 | notice, this list of conditions and the following disclaimer in the 13 | documentation and/or other materials provided with the distribution. 14 | * Neither the name of the NIPPON TELEGRAPH AND TELEPHONE CORPORATION 15 | (NTT) nor the names of its contributors may be used to endorse or 16 | promote products derived from this software without specific prior 17 | written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 23 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 25 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 26 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 27 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # pg_statsinfo: Makefile 3 | # 4 | # Copyright (c) 2009-2025, NIPPON TELEGRAPH AND TELEPHONE CORPORATION 5 | # 6 | SUBDIRS = agent reporter 7 | REGTEST = \ 8 | function-snapshot \ 9 | function-snapshot_replication \ 10 | function-logger \ 11 | function-logstore \ 12 | function-alert \ 13 | function-maintenance \ 14 | function-report \ 15 | function-command_option 16 | 17 | ifndef USE_PGXS 18 | top_builddir = ../.. 19 | makefile_global = $(top_builddir)/src/Makefile.global 20 | ifeq "$(wildcard $(makefile_global))" "" 21 | USE_PGXS = 1 # use pgxs if not in contrib directory 22 | endif 23 | endif 24 | 25 | ifdef USE_PGXS 26 | PG_CONFIG = pg_config 27 | PGXS := $(shell $(PG_CONFIG) --pgxs) 28 | include $(PGXS) 29 | else 30 | subdir = pg_statsinfo 31 | include $(makefile_global) 32 | include $(top_srcdir)/contrib/contrib-global.mk 33 | endif 34 | 35 | all install installdirs uninstall distprep clean distclean maintainer-clean: 36 | @for dir in $(SUBDIRS); do \ 37 | $(MAKE) -C $$dir $@ || exit; \ 38 | done 39 | 40 | installcheck: 41 | ( cd test && ./regress.sh $(REGTEST)) 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pg_statsinfo 2 | 3 | PostgreSQL サーバの利用統計情報を定期的に収集・蓄積することで、DB設計やPostgreSQLの運用(日々の処理傾向の把握、 性能劣化などの兆候や問題発生時の原因の把握等)に役立つツールです。 4 | 起動や終了、パラメータの設定は PostgreSQL と密に連携しており、手間をかけずに導入可能です。 5 | 6 | [日本語のマニュアルはこちら](/doc/pg_statsinfo-ja.md) 7 | 8 | [English version here](/doc/pg_statsinfo.md) 9 | 10 | ----- 11 | Copyright (c) 2009-2025, NIPPON TELEGRAPH AND TELEPHONE CORPORATION 12 | -------------------------------------------------------------------------------- /README_REGTEST: -------------------------------------------------------------------------------- 1 | How to run the regression tests. 2 | ===================================== 3 | 4 | $ gmake 5 | $ su 6 | # gmake install 7 | # exit 8 | $ gmake installcheck 9 | 10 | Requirements 11 | ------------------------------------- 12 | * pg_stat_statements should be installed for PostgreSQL 8.4 or later. 13 | 14 | $ cd /contrib/pg_stat_statements 15 | $ gmake 16 | $ su 17 | # gmake install 18 | -------------------------------------------------------------------------------- /agent/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # pg_statsinfo: Makefile 3 | # 4 | # Copyright (c) 2009-2025, NIPPON TELEGRAPH AND TELEPHONE CORPORATION 5 | # 6 | SUBDIRS = bin lib 7 | 8 | all install installdirs uninstall distprep clean distclean maintainer-clean: 9 | @for dir in $(SUBDIRS); do \ 10 | $(MAKE) -C $$dir $@ || exit; \ 11 | done 12 | -------------------------------------------------------------------------------- /agent/bin/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # pg_statsinfo: bin/Makefile 3 | # 4 | # Copyright (c) 2009-2025, NIPPON TELEGRAPH AND TELEPHONE CORPORATION 5 | # 6 | SRCS = \ 7 | pg_statsinfod.c \ 8 | autovacuum.c \ 9 | checkpoint.c \ 10 | collector.c \ 11 | collector_wait_sampling.c \ 12 | logger.c \ 13 | logger_send.c \ 14 | logger_common.c \ 15 | logger_in.c \ 16 | logger_out.c \ 17 | maintenance.c \ 18 | snapshot.c \ 19 | writer.c \ 20 | pg_control.c \ 21 | pgut/pgut.c \ 22 | pgut/pgut-list.c \ 23 | pgut/pgut-pthread.c 24 | 25 | OBJS = $(SRCS:.c=.o) 26 | DATA = pg_statsrepo.sql pg_statsrepo_alert.sql uninstall_pg_statsrepo.sql 27 | SCRIPTS = archive_pglog.sh 28 | PROGRAM = pg_statsinfod 29 | 30 | PG_CPPFLAGS = -I$(libpq_srcdir) -DFRONTEND -DPGUT_MULTI_THREADED -DPGUT_OVERRIDE_ELOG 31 | PG_LIBS = $(libpq) -lpthread 32 | 33 | ifndef USE_PGXS 34 | top_builddir = ../../../.. 35 | makefile_global = $(top_builddir)/src/Makefile.global 36 | ifeq "$(wildcard $(makefile_global))" "" 37 | USE_PGXS = 1 # use pgxs if not in contrib directory 38 | endif 39 | endif 40 | 41 | ifdef USE_PGXS 42 | PG_CONFIG = pg_config 43 | PGXS := $(shell $(PG_CONFIG) --pgxs) 44 | include $(PGXS) 45 | else 46 | subdir = contrib/$(MODULE_big) 47 | include $(makefile_global) 48 | include $(top_srcdir)/contrib/contrib-global.mk 49 | endif 50 | 51 | # exclude libraries which are not required 52 | LIBS := $(filter -lpgport -lpgcommon, $(LIBS)) 53 | -------------------------------------------------------------------------------- /agent/bin/archive_pglog.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ############################################################################# 3 | # Copyright (c) 2009-2025, NIPPON TELEGRAPH AND TELEPHONE CORPORATION 4 | ############################################################################# 5 | 6 | log_directory="${1}" 7 | archive_directory="${log_directory}/log_archive" 8 | archive_filename="${archive_directory}/$(date +postgresql-%Y%m%d.tar.gz)" 9 | 10 | # change the current directory to log directory 11 | cd "${log_directory}" || exit 1 12 | 13 | # create a directory that store archive file 14 | mkdir -p "${archive_directory}" || exit 1 15 | 16 | # search the csv log files that have been updated in more than one day ago 17 | target_files=($(find -maxdepth 1 -type f -name "*.csv" -daystart -ctime +0)) || exit 1 18 | 19 | # create archive file 20 | if [ ${#target_files[@]} -gt 0 ] ; then 21 | 22 | if [ -e "${archive_filename}" ] ; then 23 | echo "archive file \"${archive_filename}\" already exists" 1>&2 24 | exit 1 25 | fi 26 | 27 | tar cfz "${archive_filename}" ${target_files[@]} --remove-files || exit 1 28 | fi 29 | 30 | exit 0 31 | -------------------------------------------------------------------------------- /agent/bin/checkpoint.c: -------------------------------------------------------------------------------- 1 | /* 2 | * checkpoint.c 3 | * 4 | * Copyright (c) 2009-2025, NIPPON TELEGRAPH AND TELEPHONE CORPORATION 5 | */ 6 | 7 | #include "pg_statsinfod.h" 8 | 9 | #define SQL_INSERT_CHECKPOINT "\ 10 | INSERT INTO statsrepo.checkpoint VALUES \ 11 | ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)" 12 | 13 | #define NUM_CHECKPOINT_STARTING 2 14 | #define NUM_CHECKPOINT_COMPLETE 22 15 | #define NUM_RESTARTPOINT_COMPLETE 22 16 | 17 | typedef enum CheckpointType 18 | { 19 | CKPT_TYPE_CHECKPOINT, 20 | CKPT_TYPE_RESTARTPOINT 21 | } CheckpointType; 22 | 23 | /* checkpoint log data */ 24 | typedef struct CheckpointLog 25 | { 26 | QueueItem base; 27 | 28 | char start[LOGTIME_LEN]; 29 | char flags[128]; 30 | CheckpointType type; 31 | List *params; 32 | } CheckpointLog; 33 | 34 | static void Checkpoint_free(CheckpointLog *ckpt); 35 | static bool Checkpoint_exec(CheckpointLog *ckpt, PGconn *conn, const char *instid); 36 | 37 | /* 38 | * is_checkpoint 39 | */ 40 | bool 41 | is_checkpoint(const char *message) 42 | { 43 | /* log for checkpoint starting */ 44 | if (match(message, msg_checkpoint_starting)) 45 | return true; 46 | 47 | /* log for checkpoint complete */ 48 | if (match(message, msg_checkpoint_complete)) 49 | return true; 50 | 51 | /* log for restartpoint complete */ 52 | if (match(message, msg_restartpoint_complete)) 53 | return true; 54 | 55 | return false; 56 | } 57 | 58 | /* 59 | * parse_checkpoint 60 | */ 61 | bool 62 | parse_checkpoint(const char *message, const char *timestamp) 63 | { 64 | static CheckpointLog *ckpt = NULL; 65 | 66 | List *params; 67 | 68 | if ((params = capture(message, msg_checkpoint_starting, NUM_CHECKPOINT_STARTING)) != NIL) 69 | { 70 | /* log for checkpoint starting */ 71 | 72 | const char *type = (char *) list_nth(params, 0); 73 | const char *flags = (char *) list_nth(params, 1); 74 | CheckpointType ckpt_type; 75 | 76 | if (strcmp(type, "checkpoint") == 0) 77 | ckpt_type = CKPT_TYPE_CHECKPOINT; 78 | else if (strcmp(type, "restartpoint") == 0) 79 | ckpt_type = CKPT_TYPE_RESTARTPOINT; 80 | else 81 | { 82 | /* not a checkpoint log */ 83 | list_free_deep(params); 84 | return false; 85 | } 86 | 87 | /* ignore shutdown checkpoint */ 88 | if (strstr(flags, "shutdown")) 89 | { 90 | free(ckpt); 91 | ckpt = NULL; 92 | list_free_deep(params); 93 | return true; /* handled, but forget */ 94 | } 95 | 96 | if (ckpt == NULL) 97 | ckpt = pgut_new(CheckpointLog); 98 | 99 | /* copy type, flags and start timestamp */ 100 | ckpt->type = ckpt_type; 101 | strlcpy(ckpt->flags, flags, sizeof(ckpt->flags)); 102 | strlcpy(ckpt->start, timestamp, sizeof(ckpt->start)); 103 | 104 | list_free_deep(params); 105 | return true; 106 | } 107 | 108 | if ((params = capture(message, msg_checkpoint_complete, NUM_CHECKPOINT_COMPLETE)) != NIL || 109 | (params = capture(message, msg_restartpoint_complete, NUM_RESTARTPOINT_COMPLETE)) != NIL) 110 | { 111 | /* log for checkpoint complete */ 112 | 113 | /* ignore if we have not seen any checkpoint start */ 114 | if (ckpt == NULL) 115 | { 116 | list_free_deep(params); 117 | return true; /* handled, but forget */ 118 | } 119 | 120 | /* send checkpoint log to writer */ 121 | ckpt->params = params; 122 | ckpt->base.type = QUEUE_CHECKPOINT; 123 | ckpt->base.free = (QueueItemFree) Checkpoint_free; 124 | ckpt->base.exec = (QueueItemExec) Checkpoint_exec; 125 | writer_send((QueueItem *) ckpt); 126 | 127 | ckpt = NULL; 128 | 129 | return true; 130 | } 131 | 132 | /* not a checkpoint log */ 133 | return false; 134 | } 135 | 136 | static void 137 | Checkpoint_free(CheckpointLog *ckpt) 138 | { 139 | if (ckpt) 140 | { 141 | list_free_deep(ckpt->params); 142 | free(ckpt); 143 | } 144 | } 145 | 146 | static bool 147 | Checkpoint_exec(CheckpointLog *ckpt, PGconn *conn, const char *instid) 148 | { 149 | const char *params[10]; 150 | char write_duration[32]; /* for "%ld.%03d" */ 151 | char sync_duration[32]; 152 | char total_duration[32]; 153 | 154 | elog(DEBUG2, "write (checkpoint)"); 155 | 156 | params[0] = instid; /* instid */ 157 | params[1] = ckpt->start; /* start */ 158 | params[2] = ckpt->flags; /* flags */ 159 | params[3] = list_nth(ckpt->params, 0); /* num_buffers */ 160 | params[4] = list_nth(ckpt->params, 2); /* xlog_added */ 161 | params[5] = list_nth(ckpt->params, 3); /* xlog_removed */ 162 | params[6] = list_nth(ckpt->params, 4); /* xlog_recycled */ 163 | 164 | snprintf(write_duration, lengthof(write_duration), "%s.%s", 165 | (const char *) list_nth(ckpt->params, 5), 166 | (const char *) list_nth(ckpt->params, 6)); 167 | snprintf(sync_duration, lengthof(sync_duration), "%s.%s", 168 | (const char *) list_nth(ckpt->params, 7), 169 | (const char *) list_nth(ckpt->params, 8)); 170 | snprintf(total_duration, lengthof(total_duration), "%s.%s", 171 | (const char *) list_nth(ckpt->params, 9), 172 | (const char *) list_nth(ckpt->params, 10)); 173 | 174 | params[7] = write_duration; /* write_duration */ 175 | params[8] = sync_duration; /* sync_duration */ 176 | params[9] = total_duration; /* total_duration */ 177 | 178 | return pgut_command(conn, 179 | SQL_INSERT_CHECKPOINT, 10, params) == PGRES_COMMAND_OK; 180 | } 181 | -------------------------------------------------------------------------------- /agent/bin/collector_wait_sampling.c: -------------------------------------------------------------------------------- 1 | /* 2 | * collector_wait_sampling.c: 3 | * 4 | * Copyright (c) 2009-2025, NIPPON TELEGRAPH AND TELEPHONE CORPORATION 5 | */ 6 | 7 | #include "pg_statsinfod.h" 8 | 9 | #include 10 | 11 | static PGconn *collector_wait_sampling_conn = NULL; 12 | 13 | static void do_sample_wait_sampling(void); 14 | static void get_server_encoding(void); 15 | static void collector_wait_sampling_disconnect(void); 16 | extern bool extract_dbname(const char *conninfo, char *dbname, size_t size); 17 | 18 | void 19 | collector_wait_sampling_init(void) 20 | { 21 | /* do nothing */ 22 | } 23 | 24 | /* 25 | * collector_wait_sampling_main 26 | */ 27 | void * 28 | collector_wait_sampling_main(void *arg) 29 | { 30 | tim now, prev; 31 | 32 | usleep(1000 * 1000); /* wait for creating schema statsinfo. */ 33 | 34 | prev = getlocaltime_ms(); 35 | 36 | /* we set actual server encoding to libpq default params. */ 37 | get_server_encoding(); 38 | 39 | while (shutdown_state < SHUTDOWN_REQUESTED) 40 | { 41 | now = getlocaltime_ms(); 42 | 43 | /* sample wait sampling */ 44 | if (time_ms_diff(now, prev) >= wait_sampling_interval) 45 | { 46 | elog(DEBUG2, "collector_wait_sampling_main time_ms_diff %ld wait_sampling_interval %d", time_ms_diff(now, prev), wait_sampling_interval); 47 | do_sample_wait_sampling(); 48 | prev = getlocaltime_ms(); 49 | } 50 | 51 | usleep(1000); /* 1ms */ 52 | } 53 | 54 | collector_wait_sampling_disconnect(); 55 | shutdown_progress(COLLECTOR_SHUTDOWN); 56 | 57 | return NULL; 58 | } 59 | 60 | static void 61 | do_sample_wait_sampling(void) 62 | { 63 | PGconn *conn; 64 | int retry; 65 | 66 | for (retry = 0; 67 | shutdown_state < SHUTDOWN_REQUESTED && retry < DB_MAX_RETRY; 68 | delay(), retry++) 69 | { 70 | /* connect to postgres database and ensure functions are installed */ 71 | if ((conn = collector_wait_sampling_connect(NULL)) == NULL) 72 | continue; 73 | 74 | pgut_command(conn, "SELECT statsinfo.sample_wait_sampling()", 0, NULL); 75 | break; /* ok */ 76 | } 77 | } 78 | 79 | /* 80 | * set server encoding 81 | */ 82 | static void 83 | get_server_encoding(void) 84 | { 85 | PGconn *conn; 86 | int retry; 87 | const char *encode; 88 | 89 | for (retry = 0; 90 | shutdown_state < SHUTDOWN_REQUESTED && retry < DB_MAX_RETRY; 91 | delay(), retry++) 92 | { 93 | /* connect postgres database */ 94 | if ((conn = collector_wait_sampling_connect(NULL)) == NULL) 95 | continue; 96 | 97 | /* 98 | * if we could not find the encodig-string, it's ok. 99 | * because PG_SQL_ASCII was already set. 100 | */ 101 | encode = PQparameterStatus(conn, "server_encoding"); 102 | if (encode != NULL) 103 | pgut_putenv("PGCLIENTENCODING", encode); 104 | elog(DEBUG2, "collector set client_encoding : %s", encode); 105 | break; /* ok */ 106 | } 107 | } 108 | 109 | PGconn * 110 | collector_wait_sampling_connect(const char *db) 111 | { 112 | char dbname[NAMEDATALEN]; 113 | char info[1024]; 114 | const char *schema; 115 | 116 | if (db == NULL) 117 | { 118 | if (!extract_dbname(target_server, dbname, sizeof(dbname))) 119 | strncpy(dbname, "postgres", sizeof(dbname)); /* default database */ 120 | schema = "statsinfo"; 121 | } 122 | else 123 | { 124 | strncpy(dbname, db, sizeof(dbname)); 125 | /* no schema required */ 126 | schema = NULL; 127 | } 128 | 129 | /* disconnect if need to connect another database */ 130 | if (collector_wait_sampling_conn) 131 | { 132 | char *pgdb; 133 | 134 | pgdb = PQdb(collector_wait_sampling_conn); 135 | if (pgdb == NULL || strcmp(pgdb, dbname) != 0) 136 | collector_wait_sampling_disconnect(); 137 | } 138 | else 139 | { 140 | ControlFileData ctrl; 141 | 142 | readControlFile(&ctrl, data_directory); 143 | 144 | /* avoid connection fails during recovery and warm-standby */ 145 | switch (ctrl.state) 146 | { 147 | case DB_IN_PRODUCTION: 148 | case DB_IN_ARCHIVE_RECOVERY: /* hot-standby accepts connections */ 149 | break; /* ok, do connect */ 150 | default: 151 | delay(); 152 | return NULL; /* server is not ready for accepting connections */ 153 | } 154 | } 155 | 156 | #ifdef DEBUG_MODE 157 | snprintf(info, lengthof(info), "port=%s %s dbname=%s", 158 | postmaster_port, target_server, dbname); 159 | #else 160 | snprintf(info, lengthof(info), 161 | "port=%s %s dbname=%s options='-c log_statement=none'", 162 | postmaster_port, target_server, dbname); 163 | #endif 164 | return do_wait_sampling_connect(&collector_wait_sampling_conn, info, schema); 165 | } 166 | 167 | static void 168 | collector_wait_sampling_disconnect(void) 169 | { 170 | pgut_disconnect(collector_wait_sampling_conn); 171 | collector_wait_sampling_conn = NULL; 172 | } 173 | 174 | -------------------------------------------------------------------------------- /agent/bin/logger_in.c: -------------------------------------------------------------------------------- 1 | /* 2 | * logger_in.c : parse csvlog 3 | * 4 | * Copyright (c) 2009-2025, NIPPON TELEGRAPH AND TELEPHONE CORPORATION 5 | */ 6 | 7 | #include "pg_statsinfod.h" 8 | 9 | static bool badcsv(int column, const char *message); 10 | static int MatchText(const char *t, size_t tlen, 11 | const char *p, size_t plen, List **params); 12 | 13 | /* 14 | * logger_next 15 | */ 16 | 17 | #define CSV_READ_RETRY_MAX 3 18 | 19 | bool 20 | read_csv(FILE *fp, StringInfo buf, int ncolumns, size_t *columns) 21 | { 22 | int column; 23 | int retry_count; 24 | size_t read_len; 25 | size_t restart; 26 | 27 | if (buf->data == NULL) 28 | initStringInfo(buf); 29 | else 30 | resetStringInfo(buf); 31 | memset(columns, 0, ncolumns * sizeof(size_t)); 32 | column = 0; 33 | retry_count = 0; 34 | restart = 0; 35 | 36 | retry: 37 | read_len = 0; 38 | do 39 | { 40 | char *buffer; 41 | size_t len; 42 | 43 | enlargeStringInfo(buf, 1000); 44 | buffer = buf->data + buf->len; 45 | 46 | if (fgets(buffer, buf->maxlen - buf->len, fp) == NULL) 47 | break; 48 | 49 | len = strlen(buffer); 50 | if (len == 0) 51 | break; 52 | read_len += len; 53 | 54 | buf->len += len; 55 | } while (buf->data[buf->len - 1] != '\n'); 56 | 57 | if (buf->len == 0) 58 | return false; 59 | 60 | if (read_len == 0 || buf->data[buf->len - 1] != '\n') 61 | { 62 | if (retry_count < CSV_READ_RETRY_MAX) 63 | { 64 | usleep(100 * 1000); /* 100ms */ 65 | retry_count++; 66 | goto retry; 67 | } 68 | return badcsv(column + 1, buf->data); 69 | } 70 | 71 | /* split log with comma */ 72 | while (column < ncolumns) 73 | { 74 | char *buffer; 75 | char *next; 76 | 77 | buffer = buf->data + columns[column]; 78 | if (buffer[0] == '\0') 79 | break; 80 | else if (buffer[0] == '"') 81 | { 82 | /* quoted field */ 83 | if (restart) 84 | { 85 | buffer = buf->data + restart; 86 | restart = 0; 87 | } 88 | else 89 | buffer++; 90 | 91 | for (;;) 92 | { 93 | next = strchr(buffer, '"'); 94 | if (next == NULL) 95 | { 96 | /* save parse restart point */ 97 | restart = buf->len; 98 | goto retry; /* line-break in quote, needs more buffers */ 99 | } 100 | else if (next[1] == ',' || next[1] == '\n') 101 | { 102 | next[0] = '\0'; 103 | columns[column]++; 104 | columns[++column] = next - buf->data + 2; 105 | break; 106 | } 107 | else if (next[1] == '"') 108 | { 109 | /* combine "" to " */ 110 | memmove(next, next + 1, strlen(next + 1) + 1); 111 | buf->len--; 112 | buffer = next + 1; 113 | } 114 | else 115 | { 116 | return badcsv(column + 1, buf->data); 117 | } 118 | } 119 | } 120 | else 121 | { 122 | /* unquoted field */ 123 | next = strpbrk(buffer, ",\n"); 124 | if (next == NULL) 125 | { 126 | return badcsv(column + 1, buf->data); 127 | } 128 | else 129 | { 130 | next[0] = '\0'; 131 | columns[++column] = next - buf->data + 1; 132 | } 133 | } 134 | } 135 | 136 | /* throw an error if column number does not reach a necessary number. */ 137 | if (column < ncolumns) 138 | return badcsv(column + 1, buf->data); 139 | 140 | return true; 141 | } 142 | 143 | /* 144 | * badcsv - always return false. 145 | */ 146 | static bool 147 | badcsv(int column, const char *message) 148 | { 149 | elog(WARNING, "cannot parse csvlog column %d: %s", column, message); 150 | return false; 151 | } 152 | 153 | #define LIKE_TRUE 1 154 | #define LIKE_FALSE 0 155 | #define LIKE_ABORT (-1) 156 | 157 | bool 158 | match(const char *str, const char *pattern) 159 | { 160 | int r; 161 | 162 | r = MatchText(str, strlen(str), pattern, strlen(pattern), NULL); 163 | 164 | return (r == LIKE_TRUE); 165 | } 166 | 167 | List * 168 | capture(const char *str, const char *pattern, int nparams) 169 | { 170 | int r; 171 | List *params = NIL; 172 | 173 | r = MatchText(str, strlen(str), pattern, strlen(pattern), ¶ms); 174 | if (r == LIKE_TRUE && list_length(params) == nparams) 175 | return params; 176 | 177 | list_free_deep(params); 178 | return NIL; 179 | } 180 | 181 | #define pg_mblen(p) 1 182 | #define NextByte(p, plen) ((p)++, (plen)--) 183 | 184 | /* Set up to compile like_match.c for multibyte characters */ 185 | #define CHAREQ(p1, p2) wchareq((p1), (p2)) 186 | #define NextChar(p, plen) \ 187 | do { int __l = pg_mblen(p); (p) +=__l; (plen) -=__l; } while (0) 188 | 189 | static int 190 | MatchText(const char *t, size_t tlen, const char *p, size_t plen, List **params) 191 | { 192 | while (tlen > 0 && plen > 0) 193 | { 194 | if (plen < 2 || *p != '%') 195 | { 196 | /* non-wildcard pattern char fails to match text char */ 197 | if (*p != *t) 198 | return LIKE_FALSE; 199 | } 200 | else if (p[1] == '%') 201 | { 202 | /* %% is % */ 203 | NextByte(p, plen); 204 | if (*p != *t) 205 | return LIKE_FALSE; 206 | } 207 | else 208 | { 209 | const char *begin = p; 210 | const char *w = t; 211 | char firstpat; 212 | 213 | /* Skip until the type specifer */ 214 | p = strpbrk(begin + 1, "diouxXeEfFgGaAcspm"); 215 | if (p == NULL) 216 | return LIKE_FALSE; /* bad format */ 217 | p++; 218 | plen -= p - begin; 219 | if (plen <= 0) 220 | { 221 | if (params) 222 | *params = lcons(strdup_with_len(t, tlen), *params); 223 | return LIKE_TRUE; /* matches everything. */ 224 | } 225 | 226 | /* 227 | * Otherwise, scan for a text position at which we can match the 228 | * rest of the pattern. 229 | */ 230 | firstpat = *p; 231 | 232 | while (tlen > 0) 233 | { 234 | /* 235 | * Optimization to prevent most recursion: don't recurse 236 | * unless first pattern byte matches first text byte. 237 | */ 238 | if (*t == firstpat) 239 | { 240 | int matched = MatchText(t, tlen, p, plen, params); 241 | 242 | if (matched == LIKE_TRUE && params) 243 | *params = lcons(strdup_with_len(w, t - w), *params); 244 | if (matched != LIKE_FALSE) 245 | return matched; /* TRUE or ABORT */ 246 | } 247 | 248 | NextChar(t, tlen); 249 | } 250 | 251 | /* 252 | * End of text with no match, so no point in trying later places 253 | * to start matching this pattern. 254 | */ 255 | return LIKE_ABORT; 256 | } 257 | 258 | NextByte(t, tlen); 259 | NextByte(p, plen); 260 | } 261 | if (tlen > 0) 262 | return LIKE_FALSE; /* end of pattern, but not of text */ 263 | 264 | /* End of text string. Do we have matching pattern remaining? */ 265 | while (plen > 0 && *p == '%') 266 | { 267 | const char *begin = p; 268 | p = strpbrk(begin + 1, "diouxXeEfFgGaAcspm"); 269 | if (p == NULL) 270 | return LIKE_FALSE; /* bad format */ 271 | p++; 272 | plen -= p - begin; 273 | } 274 | if (plen <= 0) 275 | return LIKE_TRUE; 276 | 277 | /* 278 | * End of text with no match, so no point in trying later places to start 279 | * matching this pattern. 280 | */ 281 | return LIKE_ABORT; 282 | } 283 | -------------------------------------------------------------------------------- /agent/bin/maintenance.c: -------------------------------------------------------------------------------- 1 | /* 2 | * maintenance.c: 3 | * 4 | * Copyright (c) 2009-2025, NIPPON TELEGRAPH AND TELEPHONE CORPORATION 5 | */ 6 | 7 | #include "pg_statsinfod.h" 8 | 9 | #include 10 | #include 11 | 12 | #define SQL_DELETE_SNAPSHOT "SELECT statsrepo.del_snapshot2(CAST($1 AS TIMESTAMPTZ))" 13 | #define SQL_DELETE_REPOLOG "SELECT statsrepo.del_repolog2(CAST($1 AS TIMESTAMPTZ))" 14 | 15 | typedef struct Maintenance 16 | { 17 | QueueItem base; 18 | 19 | bool (*operation)(PGconn *conn, void *param); 20 | time_t period; 21 | } Maintenance; 22 | 23 | static bool Maintenance_exec(Maintenance *maintenance, PGconn *conn, const char *instid); 24 | static void Maintenance_free(Maintenance *maintenance); 25 | static bool delete_snapshot(PGconn *conn, void *param); 26 | static bool delete_repolog(PGconn *conn, void *param); 27 | static pid_t forkexec(const char *command, int *fd_err); 28 | 29 | /* 30 | * maintenance of the snapshot 31 | */ 32 | void 33 | maintenance_snapshot(time_t repository_keep_period) 34 | { 35 | Maintenance *maintenance; 36 | 37 | maintenance = pgut_malloc(sizeof(Maintenance)); 38 | maintenance->base.type = QUEUE_MAINTENANCE; 39 | maintenance->base.exec = (QueueItemExec) Maintenance_exec; 40 | maintenance->base.free = (QueueItemFree) Maintenance_free; 41 | maintenance->operation = delete_snapshot; 42 | maintenance->period = repository_keep_period; 43 | 44 | writer_send((QueueItem *) maintenance); 45 | } 46 | 47 | /* 48 | * maintenance of the log which is in repository 49 | */ 50 | void 51 | maintenance_repolog(time_t repolog_keep_period) 52 | { 53 | Maintenance *maintenance; 54 | 55 | maintenance = pgut_malloc(sizeof(Maintenance)); 56 | maintenance->base.type = QUEUE_MAINTENANCE; 57 | maintenance->base.exec = (QueueItemExec) Maintenance_exec; 58 | maintenance->base.free = (QueueItemFree) Maintenance_free; 59 | maintenance->operation = delete_repolog; 60 | maintenance->period = repolog_keep_period; 61 | 62 | writer_send((QueueItem *) maintenance); 63 | } 64 | 65 | /* 66 | * maintenance of the log 67 | */ 68 | pid_t 69 | maintenance_log(const char *command, int *fd_err) 70 | { 71 | char logMaintenanceCmd[MAXPGPATH]; 72 | char *dp; 73 | char *endp; 74 | const char *sp; 75 | 76 | /* construct the log maintenance command */ 77 | dp = logMaintenanceCmd; 78 | endp = logMaintenanceCmd + MAXPGPATH - 1; 79 | *endp = '\0'; 80 | 81 | for (sp = log_maintenance_command; *sp; sp++) 82 | { 83 | if (*sp == '%') 84 | { 85 | switch (sp[1]) 86 | { 87 | case 'l': 88 | /* %l: log directory */ 89 | sp++; 90 | if (is_absolute_path(log_directory)) 91 | strlcpy(dp, log_directory, endp - dp); 92 | else 93 | join_path_components(dp, data_directory, log_directory); 94 | dp += strlen(dp); 95 | break; 96 | case '%': 97 | /* convert %% to a single % */ 98 | sp++; 99 | if (dp < endp) 100 | *dp++ = *sp; 101 | break; 102 | default: 103 | /* otherwise treat the % as not special */ 104 | if (dp < endp) 105 | *dp++ = *sp; 106 | break; 107 | } 108 | } 109 | else 110 | { 111 | if (dp < endp) 112 | *dp++ = *sp; 113 | } 114 | } 115 | *dp = '\0'; 116 | 117 | /* run the log maintenance command in background */ 118 | return forkexec(logMaintenanceCmd, fd_err); 119 | } 120 | 121 | #define ERROR_MESSAGE_MAXSIZE 256 122 | 123 | /* 124 | * check the status of log maintenance command running in background 125 | */ 126 | bool 127 | check_maintenance_log(pid_t log_maintenance_pid, int fd_err) 128 | { 129 | int status; 130 | 131 | switch (waitpid(log_maintenance_pid, &status, WNOHANG)) 132 | { 133 | case -1: /* error */ 134 | elog(ERROR, 135 | "failed to wait of the log maintenance command: %s", strerror(errno)); 136 | close(fd_err); 137 | return true; 138 | case 0: /* running */ 139 | elog(DEBUG2, "log maintenance command is running"); 140 | return false; 141 | default: /* completed */ 142 | if (status != 0) 143 | { 144 | /* command exit value is abnormally code */ 145 | ssize_t read_size; 146 | char errmsg[ERROR_MESSAGE_MAXSIZE]; 147 | 148 | if((read_size = read(fd_err, errmsg, sizeof(errmsg) - 1)) >= 0) 149 | errmsg[read_size] = '\0'; 150 | else 151 | { 152 | elog(ERROR, "read() on self-pipe failed: %s", strerror(errno)); 153 | errmsg[0] = '\0'; 154 | } 155 | 156 | if (WIFEXITED(status)) 157 | elog(ERROR, 158 | "log maintenance command failed with exit code %d: %s", 159 | WEXITSTATUS(status), errmsg); 160 | else if (WIFSIGNALED(status)) 161 | elog(ERROR, 162 | "log maintenance command was terminated by signal %d: %s", 163 | WTERMSIG(status), errmsg); 164 | else 165 | elog(ERROR, 166 | "log maintenance command exited with unrecognized status %d: %s", 167 | status, errmsg); 168 | } 169 | close(fd_err); 170 | return true; 171 | } 172 | } 173 | 174 | static bool 175 | delete_snapshot(PGconn *conn, void *param) 176 | { 177 | const char *params[1]; 178 | ExecStatusType status; 179 | 180 | params[0] = (const char *) param; 181 | 182 | /* exclusive control during snapshot and maintenance */ 183 | pthread_mutex_lock(&maintenance_lock); 184 | status = pgut_command(conn, SQL_DELETE_SNAPSHOT, 1, params); 185 | pthread_mutex_unlock(&maintenance_lock); 186 | 187 | return status == PGRES_TUPLES_OK; 188 | } 189 | 190 | static bool 191 | delete_repolog(PGconn *conn, void *param) 192 | { 193 | const char *params[1]; 194 | 195 | params[0] = (const char *) param; 196 | if (pgut_command(conn, SQL_DELETE_REPOLOG, 1, params) != PGRES_TUPLES_OK) 197 | return false; 198 | 199 | return true; 200 | } 201 | 202 | static bool 203 | Maintenance_exec(Maintenance *maintenance, PGconn *conn, const char *instid) 204 | { 205 | char timestamp[32]; 206 | 207 | strftime(timestamp, sizeof(timestamp), 208 | "%Y-%m-%d %H:%M:%S", localtime(&maintenance->period)); 209 | 210 | return maintenance->operation(conn, timestamp); 211 | } 212 | 213 | static void 214 | Maintenance_free(Maintenance *maintenance) 215 | { 216 | free(maintenance); 217 | } 218 | 219 | #define R (0) 220 | #define W (1) 221 | 222 | /* 223 | * execute a shell command asynchronously 224 | */ 225 | static pid_t 226 | forkexec(const char *command, int *fd_err) 227 | { 228 | pid_t cpid; 229 | int pipe_fd_err[2]; 230 | 231 | /* create pipes */ 232 | if (pipe(pipe_fd_err) < 0) 233 | { 234 | elog(ERROR, "could not create pipe: %s", strerror(errno)); 235 | return -1; 236 | } 237 | 238 | /* invoke processs */ 239 | if ((cpid = fork()) < 0) 240 | { 241 | close(pipe_fd_err[R]); 242 | close(pipe_fd_err[W]); 243 | elog(ERROR, "fork failed: %s", strerror(errno)); 244 | return -1; 245 | } 246 | 247 | if (cpid == 0) 248 | { 249 | /* in child process */ 250 | close(pipe_fd_err[R]); 251 | dup2(pipe_fd_err[W], STDERR_FILENO); 252 | close(pipe_fd_err[W]); 253 | 254 | execlp("/bin/sh", "sh", "-c", command, NULL); 255 | _exit(127); 256 | } 257 | 258 | close(pipe_fd_err[W]); 259 | 260 | *fd_err = pipe_fd_err[R]; 261 | return cpid; 262 | } 263 | -------------------------------------------------------------------------------- /agent/bin/pg_control.c: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | * 3 | * pg_control.c 4 | * 5 | * Copyright (c) 2009-2025, NIPPON TELEGRAPH AND TELEPHONE CORPORATION 6 | * 7 | *------------------------------------------------------------------------- 8 | */ 9 | 10 | #include "c.h" 11 | #include "../common.h" 12 | 13 | #include 14 | #include 15 | 16 | bool 17 | readControlFile(ControlFileData *ctrl, const char *pgdata) 18 | { 19 | char path[MAXPGPATH]; 20 | int fd; 21 | pg_crc32 crc; 22 | 23 | snprintf(path, MAXPGPATH, "%s/global/pg_control", pgdata); 24 | 25 | if ((fd = open(path, O_RDONLY | PG_BINARY, 0)) == -1) 26 | return false; 27 | 28 | if (read(fd, ctrl, sizeof(ControlFileData)) != sizeof(ControlFileData)) 29 | return false; 30 | close(fd); 31 | 32 | /* Check the CRC. */ 33 | INIT_CRC32(crc); 34 | COMP_CRC32(crc, 35 | (char *) ctrl, 36 | offsetof(ControlFileData, crc)); 37 | FIN_CRC32(crc); 38 | 39 | if (!EQ_CRC32(crc, ctrl->crc)) 40 | return false; 41 | 42 | return true; 43 | } 44 | 45 | #ifdef FRONTEND 46 | /* 47 | * copied from pg_crc.c. 48 | * 49 | * This table is based on the polynomial 50 | * x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1. 51 | * (This is the same polynomial used in Ethernet checksums, for instance.) 52 | */ 53 | const uint32 pg_crc32_table[256] = { 54 | 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 55 | 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, 56 | 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 57 | 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 58 | 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 59 | 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 60 | 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 61 | 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 62 | 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 63 | 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 64 | 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 65 | 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 66 | 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 67 | 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, 68 | 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 69 | 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 70 | 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 71 | 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, 72 | 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 73 | 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, 74 | 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 75 | 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 76 | 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 77 | 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 78 | 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 79 | 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 80 | 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 81 | 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 82 | 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 83 | 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, 84 | 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 85 | 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, 86 | 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 87 | 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 88 | 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 89 | 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 90 | 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 91 | 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, 92 | 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 93 | 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 94 | 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 95 | 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, 96 | 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 97 | 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, 98 | 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 99 | 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 100 | 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 101 | 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, 102 | 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 103 | 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, 104 | 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 105 | 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 106 | 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 107 | 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, 108 | 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 109 | 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, 110 | 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 111 | 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 112 | 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 113 | 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, 114 | 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 115 | 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, 116 | 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 117 | 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D 118 | }; 119 | #endif 120 | -------------------------------------------------------------------------------- /agent/bin/pgut/pgut-list.h: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | * 3 | * pgut-list.h : copied from postgres/nodes/pg_list.h 4 | * 5 | * Copyright (c) 2009-2025, NIPPON TELEGRAPH AND TELEPHONE CORPORATION 6 | * 7 | *------------------------------------------------------------------------- 8 | */ 9 | 10 | #ifndef PGUT_LIST_H 11 | #define PGUT_LIST_H 12 | 13 | #include "nodes/nodes.h" 14 | 15 | typedef struct ListCell ListCell; 16 | 17 | typedef struct List 18 | { 19 | NodeTag type; /* T_List, T_IntList, or T_OidList */ 20 | int length; 21 | ListCell *head; 22 | ListCell *tail; 23 | } List; 24 | 25 | struct ListCell 26 | { 27 | union 28 | { 29 | void *ptr_value; 30 | int int_value; 31 | Oid oid_value; 32 | } data; 33 | ListCell *next; 34 | }; 35 | 36 | /* 37 | * The *only* valid representation of an empty list is NIL; in other 38 | * words, a non-NIL list is guaranteed to have length >= 1 and 39 | * head/tail != NULL 40 | */ 41 | #define NIL ((List *) NULL) 42 | 43 | /* 44 | * These routines are used frequently. However, we can't implement 45 | * them as macros, since we want to avoid double-evaluation of macro 46 | * arguments. Therefore, we implement them using static inline functions 47 | * if supported by the compiler, or as regular functions otherwise. 48 | */ 49 | #ifndef __GNUC__ 50 | extern ListCell *list_head(const List *l); 51 | extern ListCell *list_tail(List *l); 52 | extern int list_length(const List *l); 53 | #else 54 | static inline ListCell * 55 | list_head(const List *l) 56 | { 57 | return l ? l->head : NULL; 58 | } 59 | 60 | static inline ListCell * 61 | list_tail(List *l) 62 | { 63 | return l ? l->tail : NULL; 64 | } 65 | 66 | static inline int 67 | list_length(const List *l) 68 | { 69 | return l ? l->length : 0; 70 | } 71 | #endif /* ! __GNUC__ */ 72 | 73 | /* 74 | * NB: There is an unfortunate legacy from a previous incarnation of 75 | * the List API: the macro lfirst() was used to mean "the data in this 76 | * cons cell". To avoid changing every usage of lfirst(), that meaning 77 | * has been kept. As a result, lfirst() takes a ListCell and returns 78 | * the data it contains; to get the data in the first cell of a 79 | * List, use linitial(). Worse, lsecond() is more closely related to 80 | * linitial() than lfirst(): given a List, lsecond() returns the data 81 | * in the second cons cell. 82 | */ 83 | #define lnext(lc) ((lc)->next) 84 | #define lfirst(lc) ((lc)->data.ptr_value) 85 | #define lfirst_int(lc) ((lc)->data.int_value) 86 | #define lfirst_oid(lc) ((lc)->data.oid_value) 87 | 88 | #define linitial(l) lfirst(list_head(l)) 89 | #define linitial_int(l) lfirst_int(list_head(l)) 90 | #define linitial_oid(l) lfirst_oid(list_head(l)) 91 | 92 | #define lsecond(l) lfirst(lnext(list_head(l))) 93 | #define lsecond_int(l) lfirst_int(lnext(list_head(l))) 94 | #define lsecond_oid(l) lfirst_oid(lnext(list_head(l))) 95 | 96 | #define lthird(l) lfirst(lnext(lnext(list_head(l)))) 97 | #define lthird_int(l) lfirst_int(lnext(lnext(list_head(l)))) 98 | #define lthird_oid(l) lfirst_oid(lnext(lnext(list_head(l)))) 99 | 100 | #define lfourth(l) lfirst(lnext(lnext(lnext(list_head(l))))) 101 | #define lfourth_int(l) lfirst_int(lnext(lnext(lnext(list_head(l))))) 102 | #define lfourth_oid(l) lfirst_oid(lnext(lnext(lnext(list_head(l))))) 103 | 104 | #define llast(l) lfirst(list_tail(l)) 105 | #define llast_int(l) lfirst_int(list_tail(l)) 106 | #define llast_oid(l) lfirst_oid(list_tail(l)) 107 | 108 | /* 109 | * foreach - 110 | * a convenience macro which loops through the list 111 | */ 112 | #define foreach(cell, l) \ 113 | for ((cell) = list_head(l); (cell) != NULL; (cell) = lnext(cell)) 114 | 115 | /* 116 | * for_each_cell - 117 | * a convenience macro which loops through a list starting from a 118 | * specified cell 119 | */ 120 | #define for_each_cell(cell, initcell) \ 121 | for ((cell) = (initcell); (cell) != NULL; (cell) = lnext(cell)) 122 | 123 | /* 124 | * forboth - 125 | * a convenience macro for advancing through two linked lists 126 | * simultaneously. This macro loops through both lists at the same 127 | * time, stopping when either list runs out of elements. Depending 128 | * on the requirements of the call site, it may also be wise to 129 | * assert that the lengths of the two lists are equal. 130 | */ 131 | #define forboth(cell1, list1, cell2, list2) \ 132 | for ((cell1) = list_head(list1), (cell2) = list_head(list2); \ 133 | (cell1) != NULL && (cell2) != NULL; \ 134 | (cell1) = lnext(cell1), (cell2) = lnext(cell2)) 135 | 136 | /* 137 | * forthree - 138 | * the same for three lists 139 | */ 140 | #define forthree(cell1, list1, cell2, list2, cell3, list3) \ 141 | for ((cell1) = list_head(list1), (cell2) = list_head(list2), (cell3) = list_head(list3); \ 142 | (cell1) != NULL && (cell2) != NULL && (cell3) != NULL; \ 143 | (cell1) = lnext(cell1), (cell2) = lnext(cell2), (cell3) = lnext(cell3)) 144 | 145 | extern List *lappend(List *list, void *datum); 146 | extern ListCell *lappend_cell(List *list, ListCell *prev, void *datum); 147 | extern List *lcons(void *datum, List *list); 148 | extern List *list_concat(List *list1, List *list2); 149 | extern List *list_truncate(List *list, int new_size); 150 | extern void *list_nth(const List *list, int n); 151 | extern bool list_member_ptr(const List *list, const void *datum); 152 | extern List *list_delete_ptr(List *list, void *datum); 153 | extern List *list_delete_first(List *list); 154 | extern List *list_delete_cell(List *list, ListCell *cell, ListCell *prev); 155 | extern void list_free(List *list); 156 | extern void list_free_deep(List *list); 157 | extern List *list_copy(const List *list); 158 | extern List *list_copy_tail(const List *list, int nskip); 159 | 160 | /* 161 | * pgut-list.c : Extended list functions 162 | */ 163 | extern void list_walk(List *list, void (*walker)()); 164 | extern void list_destroy(List *list, void (*walker)()); 165 | 166 | #endif /* PGUT_LIST_H */ 167 | -------------------------------------------------------------------------------- /agent/bin/pgut/pgut-pthread.c: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | * 3 | * pgut-pthread.c: Portable pthread implementation and support functions. 4 | * 5 | * Copyright (c) 2009-2025, NIPPON TELEGRAPH AND TELEPHONE CORPORATION 6 | * 7 | *------------------------------------------------------------------------- 8 | */ 9 | 10 | #include "pgut.h" 11 | #include "pgut-pthread.h" 12 | 13 | void 14 | pgut_mutex_lock(pthread_mutex_t *mutex) 15 | { 16 | for (;;) 17 | { 18 | CHECK_FOR_INTERRUPTS(); 19 | 20 | errno = pthread_mutex_lock(mutex); 21 | if (errno == 0) 22 | break; 23 | else if (errno != EINTR) 24 | ereport(PANIC, 25 | (errcode_errno(), 26 | errmsg("pthread_mutex_lock: "))); 27 | } 28 | } 29 | 30 | void 31 | pgut_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) 32 | { 33 | for (;;) 34 | { 35 | CHECK_FOR_INTERRUPTS(); 36 | 37 | errno = pthread_cond_wait(cond, mutex); 38 | if (errno == 0) 39 | break; 40 | else if (errno != EINTR) 41 | ereport(PANIC, 42 | (errcode_errno(), 43 | errmsg("pthread_cond_wait: "))); 44 | } 45 | } 46 | 47 | #ifdef WIN32 48 | 49 | typedef struct win32_pthread 50 | { 51 | HANDLE handle; 52 | void *(*routine)(void *); 53 | void *arg; 54 | void *result; 55 | } win32_pthread; 56 | 57 | /* allocated on thread local storage */ 58 | __declspec(thread) static pthread_t self_thread; 59 | 60 | static unsigned __stdcall 61 | win32_pthread_run(void *arg) 62 | { 63 | win32_pthread *th = (win32_pthread *) arg; 64 | 65 | self_thread = th; 66 | th->result = th->routine(th->arg); 67 | 68 | return 0; 69 | } 70 | 71 | #define DETACHSTATE_MASK (PTHREAD_CREATE_JOINABLE | PTHREAD_CREATE_DETACHED) 72 | 73 | static int 74 | maperr(void) 75 | { 76 | _dosmaperr(GetLastError()); 77 | return errno; 78 | } 79 | 80 | pthread_t 81 | pthread_self(void) 82 | { 83 | return self_thread; 84 | } 85 | 86 | int 87 | pthread_create(pthread_t *thread, 88 | pthread_attr_t *attr, 89 | void * (*start_routine)(void *), 90 | void *arg) 91 | { 92 | int save_errno; 93 | win32_pthread *th; 94 | 95 | if ((th = malloc(sizeof(win32_pthread))) == NULL) 96 | return errno = ENOMEM; 97 | th->routine = start_routine; 98 | th->arg = arg; 99 | th->result = NULL; 100 | 101 | th->handle = (HANDLE) _beginthreadex(NULL, 0, win32_pthread_run, th, 0, NULL); 102 | if (th->handle == NULL) 103 | { 104 | save_errno = errno; 105 | free(th); 106 | return save_errno; 107 | } 108 | 109 | if (attr && (*attr & DETACHSTATE_MASK) == PTHREAD_CREATE_DETACHED) 110 | { 111 | CloseHandle(th->handle); 112 | th->handle = NULL; 113 | } 114 | 115 | *thread = th; 116 | return 0; 117 | } 118 | 119 | int 120 | pthread_detach(pthread_t th) 121 | { 122 | if (th == NULL) 123 | return 0; 124 | 125 | if (th->handle != NULL && !CloseHandle(th->handle)) 126 | return maperr(); 127 | 128 | free(th); 129 | return 0; 130 | } 131 | 132 | int 133 | pthread_join(pthread_t th, void **thread_return) 134 | { 135 | if (th == NULL || th->handle == NULL) 136 | return errno = EINVAL; 137 | 138 | if (WaitForSingleObject(th->handle, INFINITE) != WAIT_OBJECT_0) 139 | return maperr(); 140 | 141 | if (thread_return) 142 | *thread_return = th->result; 143 | 144 | CloseHandle(th->handle); 145 | free(th); 146 | return 0; 147 | } 148 | 149 | int 150 | pthread_attr_init(pthread_attr_t *attr) 151 | { 152 | *attr = 0; 153 | return 0; 154 | } 155 | 156 | int 157 | pthread_attr_destroy(pthread_attr_t *attr) 158 | { 159 | /* do nothing */ 160 | return 0; 161 | } 162 | 163 | int 164 | pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate) 165 | { 166 | *attr = ((*attr & ~DETACHSTATE_MASK) | detachstate); 167 | return 0; 168 | } 169 | 170 | int 171 | pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate) 172 | { 173 | *detachstate = (*attr & DETACHSTATE_MASK); 174 | return 0; 175 | } 176 | 177 | int 178 | pthread_mutex_init(pthread_mutex_t *mutex, void *attr) 179 | { 180 | InitializeCriticalSection(mutex); 181 | return 0; 182 | } 183 | 184 | int 185 | pthread_mutex_destroy(pthread_mutex_t *mutex) 186 | { 187 | /* do nothing */ 188 | return 0; 189 | } 190 | 191 | int 192 | pthread_mutex_lock(pthread_mutex_t *mutex) 193 | { 194 | EnterCriticalSection(mutex); 195 | return 0; 196 | } 197 | 198 | int 199 | pthread_mutex_unlock(pthread_mutex_t *mutex) 200 | { 201 | LeaveCriticalSection(mutex); 202 | return 0; 203 | } 204 | 205 | int 206 | pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr) 207 | { 208 | if (cond_attr != NULL) 209 | return errno = EINVAL; /* cond_attr is not supported for now. */ 210 | if ((*cond = CreateEvent(NULL, FALSE, FALSE, NULL)) == NULL) 211 | return maperr(); 212 | return 0; 213 | } 214 | 215 | int 216 | pthread_cond_signal(pthread_cond_t *cond) 217 | { 218 | /* single wakeup is not supported for now. */ 219 | return pthread_cond_broadcast(cond); 220 | } 221 | 222 | int 223 | pthread_cond_broadcast(pthread_cond_t *cond) 224 | { 225 | if (!SetEvent(*cond)) 226 | return maperr(); 227 | return 0; 228 | } 229 | 230 | int 231 | pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) 232 | { 233 | int ret; 234 | 235 | if ((ret = pthread_mutex_unlock(mutex)) != 0) 236 | return ret; 237 | if (WaitForSingleObject(*cond, INFINITE) != WAIT_OBJECT_0) 238 | return maperr(); 239 | if ((ret = pthread_mutex_lock(mutex)) != 0) 240 | return ret; 241 | return 0; 242 | } 243 | 244 | int 245 | pthread_cond_destroy(pthread_cond_t *cond) 246 | { 247 | if (cond && *cond && !CloseHandle(*cond)) 248 | return maperr(); 249 | return 0; 250 | } 251 | 252 | int 253 | pthread_key_create(pthread_key_t *key, void (*destr_function) (void *)) 254 | { 255 | Assert(destr_function == NULL); /* not supported */ 256 | 257 | if ((*key = TlsAlloc()) != 0xFFFFFFFF) 258 | return 0; 259 | else 260 | return maperr(); 261 | } 262 | 263 | int 264 | pthread_key_delete(pthread_key_t key) 265 | { 266 | if (TlsFree(key)) 267 | return 0; 268 | else 269 | return maperr(); 270 | } 271 | 272 | int 273 | pthread_setspecific(pthread_key_t key, const void *pointer) 274 | { 275 | if (TlsSetValue(key, (void *) pointer)) 276 | return 0; 277 | else 278 | return maperr(); 279 | } 280 | 281 | void * 282 | pthread_getspecific(pthread_key_t key) 283 | { 284 | return TlsGetValue(key); 285 | } 286 | 287 | #endif 288 | -------------------------------------------------------------------------------- /agent/bin/pgut/pgut-pthread.h: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | * 3 | * pgut-pthread.h 4 | * 5 | * Copyright (c) 2009-2025, NIPPON TELEGRAPH AND TELEPHONE CORPORATION 6 | * 7 | *------------------------------------------------------------------------- 8 | */ 9 | 10 | #ifndef PGUT_PTHREAD_H 11 | #define PGUT_PTHREAD_H 12 | 13 | #ifndef WIN32 14 | 15 | #include 16 | 17 | #else 18 | 19 | struct win32_pthread; 20 | 21 | typedef struct win32_pthread *pthread_t; 22 | typedef int pthread_attr_t; 23 | typedef DWORD pthread_key_t; 24 | typedef CRITICAL_SECTION pthread_mutex_t; 25 | typedef void *pthread_cond_t; 26 | typedef int pthread_condattr_t; 27 | typedef DWORD pthread_key_t; 28 | 29 | #define PTHREAD_CREATE_JOINABLE 0x0 30 | #define PTHREAD_CREATE_DETACHED 0x1 31 | 32 | extern pthread_t pthread_self(void); 33 | extern int pthread_create(pthread_t *thread, pthread_attr_t *attr, void * (*start_routine)(void *), void *arg); 34 | extern int pthread_detach(pthread_t th); 35 | extern int pthread_join(pthread_t th, void **thread_return); 36 | extern int pthread_attr_init(pthread_attr_t *attr); 37 | extern int pthread_attr_destroy(pthread_attr_t *attr); 38 | extern int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate); 39 | extern int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate); 40 | 41 | extern int pthread_mutex_init(pthread_mutex_t *mutex, void *attr); 42 | extern int pthread_mutex_destroy(pthread_mutex_t *mutex); 43 | extern int pthread_mutex_lock(pthread_mutex_t *mutex); 44 | extern int pthread_mutex_unlock(pthread_mutex_t *mutex); 45 | 46 | extern int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr); 47 | extern int pthread_cond_signal(pthread_cond_t *cond); 48 | extern int pthread_cond_broadcast(pthread_cond_t *cond); 49 | extern int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex); 50 | extern int pthread_cond_destroy(pthread_cond_t *cond); 51 | 52 | extern int pthread_key_create(pthread_key_t *key, void (*destr_function) (void *)); 53 | extern int pthread_key_delete(pthread_key_t key); 54 | extern int pthread_setspecific(pthread_key_t key, const void *pointer); 55 | extern void * pthread_getspecific(pthread_key_t key); 56 | 57 | #endif 58 | 59 | extern void pgut_mutex_lock(pthread_mutex_t *mutex); 60 | extern void pgut_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex); 61 | 62 | #endif /* PGUT_PTHREAD_H */ 63 | -------------------------------------------------------------------------------- /agent/bin/pgut/pgut.h: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | * 3 | * pgut.h 4 | * 5 | * Copyright (c) 2009-2025, NIPPON TELEGRAPH AND TELEPHONE CORPORATION 6 | * 7 | *------------------------------------------------------------------------- 8 | */ 9 | 10 | #ifndef PGUT_H 11 | #define PGUT_H 12 | 13 | #include "c.h" 14 | #include 15 | 16 | #ifndef WIN32 17 | #include 18 | #include 19 | #endif 20 | 21 | #include "libpq-fe.h" 22 | #include "pqexpbuffer.h" 23 | #include "utils/elog.h" 24 | 25 | #define INFINITE_STR "INFINITE" 26 | 27 | typedef enum YesNo 28 | { 29 | DEFAULT, 30 | NO, 31 | YES 32 | } YesNo; 33 | 34 | typedef void (*pgut_atexit_callback)(bool fatal, void *userdata); 35 | 36 | /* 37 | * pgut client variables and functions 38 | */ 39 | extern const char *PROGRAM_NAME; 40 | extern const char *PROGRAM_VERSION; 41 | extern const char *PROGRAM_URL; 42 | extern const char *PROGRAM_EMAIL; 43 | 44 | /* 45 | * pgut framework variables and functions 46 | */ 47 | extern bool interrupted; 48 | extern int pgut_log_level; 49 | extern int pgut_abort_level; 50 | extern int pgut_abort_code; 51 | extern bool pgut_echo; 52 | 53 | extern void pgut_init(int argc, char **argv); 54 | extern void pgut_atexit_push(pgut_atexit_callback callback, void *userdata); 55 | extern void pgut_atexit_pop(pgut_atexit_callback callback, void *userdata); 56 | extern void pgut_putenv(const char *key, const char *value); 57 | 58 | /* 59 | * Database connections 60 | */ 61 | extern PGconn *pgut_connect(const char *info, YesNo prompt, int elevel); 62 | extern void pgut_disconnect(PGconn *conn); 63 | extern void pgut_disconnect_all(void); 64 | extern PGresult *pgut_execute(PGconn* conn, const char *query, int nParams, const char **params); 65 | PGresult *pgut_execute_elevel(PGconn* conn, const char *query, int nParams, const char **params, int elevel); 66 | extern ExecStatusType pgut_command(PGconn* conn, const char *query, int nParams, const char **params); 67 | extern bool pgut_commit(PGconn *conn); 68 | extern void pgut_rollback(PGconn *conn); 69 | extern bool pgut_send(PGconn* conn, const char *query, int nParams, const char **params); 70 | extern int pgut_wait(int num, PGconn *connections[], struct timeval *timeout); 71 | 72 | /* 73 | * memory allocators 74 | */ 75 | extern void *pgut_malloc(size_t size); 76 | extern void *pgut_realloc(void *p, size_t size); 77 | extern char *pgut_strdup(const char *str); 78 | extern char *strdup_with_len(const char *str, size_t len); 79 | extern char *strdup_trim(const char *str); 80 | 81 | #define pgut_new(type) ((type *) pgut_malloc(sizeof(type))) 82 | #define pgut_newarray(type, n) ((type *) pgut_malloc(sizeof(type) * (n))) 83 | #define pgut_newvar(type, m, n) ((type *) pgut_malloc(offsetof(type, m) + (n))) 84 | 85 | /* 86 | * file operations 87 | */ 88 | extern FILE *pgut_fopen(const char *path, const char *mode); 89 | extern bool pgut_mkdir(const char *path); 90 | 91 | /* 92 | * elog 93 | */ 94 | #define E_PG_CONNECT (-1) /* PostgreSQL connection error */ 95 | #define E_PG_COMMAND (-2) /* PostgreSQL query or command error */ 96 | 97 | #undef elog 98 | #undef ereport 99 | #define ereport(elevel, rest) \ 100 | (pgut_errstart(elevel) ? (pgut_errfinish rest) : (void) 0) 101 | 102 | extern void elog(int elevel, const char *fmt, ...) 103 | __attribute__((format(printf, 2, 3))); 104 | extern const char *format_elevel(int elevel); 105 | extern int parse_elevel(const char *value); 106 | extern int errcode_errno(void); 107 | extern bool log_required(int elevel, int log_min_level); 108 | extern bool pgut_errstart(int elevel); 109 | extern void pgut_errfinish(int dummy, ...); 110 | extern void pgut_error(int elevel, int code, const char *msg, const char *detail); 111 | 112 | /* 113 | * CHECK_FOR_INTERRUPTS 114 | */ 115 | #undef CHECK_FOR_INTERRUPTS 116 | extern void CHECK_FOR_INTERRUPTS(void); 117 | 118 | /* 119 | * Assert 120 | */ 121 | #undef Assert 122 | #undef AssertArg 123 | #undef AssertMacro 124 | 125 | #ifdef USE_ASSERT_CHECKING 126 | #define Assert(x) assert(x) 127 | #define AssertArg(x) assert(x) 128 | #define AssertMacro(x) assert(x) 129 | #else 130 | #define Assert(x) ((void) 0) 131 | #define AssertArg(x) ((void) 0) 132 | #define AssertMacro(x) ((void) 0) 133 | #endif 134 | 135 | /* 136 | * StringInfo and string operations 137 | */ 138 | #define STRINGINFO_H 139 | 140 | #define StringInfoData PQExpBufferData 141 | #define StringInfo PQExpBuffer 142 | #define makeStringInfo createPQExpBuffer 143 | #define initStringInfo initPQExpBuffer 144 | #define freeStringInfo destroyPQExpBuffer 145 | #define termStringInfo termPQExpBuffer 146 | #define resetStringInfo resetPQExpBuffer 147 | #define enlargeStringInfo enlargePQExpBuffer 148 | #define printfStringInfo printfPQExpBuffer /* reset + append */ 149 | #define appendStringInfo appendPQExpBuffer 150 | #define appendStringInfoString appendPQExpBufferStr 151 | #define appendStringInfoChar appendPQExpBufferChar 152 | #define appendBinaryStringInfo appendBinaryPQExpBuffer 153 | 154 | extern bool appendStringInfoVA2(StringInfo str, const char *fmt, va_list args) 155 | __attribute__((format(PG_PRINTF_ATTRIBUTE, 2, 0))); 156 | extern int appendStringInfoFile(StringInfo str, FILE *fp); 157 | extern int appendStringInfoFd(StringInfo str, int fd); 158 | 159 | extern bool parse_bool(const char *value, bool *result); 160 | extern bool parse_bool_with_len(const char *value, size_t len, bool *result); 161 | extern bool parse_int32(const char *value, int32 *result); 162 | extern bool parse_uint32(const char *value, uint32 *result); 163 | extern bool parse_int64(const char *value, int64 *result); 164 | extern bool parse_uint64(const char *value, uint64 *result); 165 | extern bool parse_time(const char *value, time_t *time); 166 | 167 | #define IsSpace(c) (isspace((unsigned char)(c))) 168 | #define IsAlpha(c) (isalpha((unsigned char)(c))) 169 | #define IsAlnum(c) (isalnum((unsigned char)(c))) 170 | #define IsIdentHead(c) (IsAlpha(c) || (c) == '_') 171 | #define IsIdentBody(c) (IsAlnum(c) || (c) == '_') 172 | #define ToLower(c) (tolower((unsigned char)(c))) 173 | #define ToUpper(c) (toupper((unsigned char)(c))) 174 | 175 | /* 176 | * socket operations 177 | */ 178 | extern int wait_for_socket(int sock, struct timeval *timeout); 179 | extern int wait_for_sockets(int nfds, fd_set *fds, struct timeval *timeout); 180 | 181 | #ifdef WIN32 182 | extern int sleep(unsigned int seconds); 183 | extern int usleep(unsigned int usec); 184 | #endif 185 | 186 | #endif /* PGUT_H */ 187 | -------------------------------------------------------------------------------- /agent/bin/uninstall_pg_statsrepo.sql: -------------------------------------------------------------------------------- 1 | /* 2 | * bin/uninstall_pg_statsrepo.sql 3 | * 4 | * Copyright (c) 2009-2025, NIPPON TELEGRAPH AND TELEPHONE CORPORATION 5 | */ 6 | 7 | DROP SCHEMA IF EXISTS statsrepo CASCADE; 8 | -------------------------------------------------------------------------------- /agent/bin/writer_sql.h: -------------------------------------------------------------------------------- 1 | /* 2 | * writer_sql.h 3 | * 4 | * Copyright (c) 2009-2025, NIPPON TELEGRAPH AND TELEPHONE CORPORATION 5 | */ 6 | 7 | #ifndef WRITER_SQL_H 8 | #define WRITER_SQL_H 9 | 10 | /* 11 | * snapshot query 12 | */ 13 | 14 | #define SQL_NEW_SNAPSHOT "\ 15 | INSERT INTO statsrepo.snapshot(instid, time, comment) VALUES \ 16 | ($1, $2, $3) RETURNING snapid, CAST(time AS DATE)" 17 | 18 | #define SQL_INSERT_DATABASE "\ 19 | INSERT INTO statsrepo.database VALUES \ 20 | ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24)" 21 | 22 | #define SQL_INSERT_TABLESPACE "\ 23 | INSERT INTO statsrepo.tablespace VALUES \ 24 | ($1, $2, $3, $4, $5, $6, $7, $8)" 25 | 26 | #define SQL_INSERT_ACTIVITY "\ 27 | INSERT INTO statsrepo.activity VALUES \ 28 | ($1, $2, $3, $4, $5, $6)" 29 | 30 | #define SQL_INSERT_LONG_TRANSACTION "\ 31 | INSERT INTO statsrepo.xact VALUES \ 32 | ($1, $2, $3, $4, $5, $6)" 33 | 34 | #define SQL_INSERT_STATEMENT "\ 35 | INSERT INTO statsrepo.statement \ 36 | SELECT (($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25, $26)::statsrepo.statement).* \ 37 | FROM statsrepo.database d \ 38 | WHERE d.snapid = $1 AND d.dbid = $2" 39 | 40 | #define SQL_INSERT_PLAN "\ 41 | INSERT INTO statsrepo.plan \ 42 | SELECT (($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25, $26, $27)::statsrepo.plan).* \ 43 | FROM statsrepo.database d \ 44 | WHERE d.snapid = $1 AND d.dbid = $2" 45 | 46 | #define SQL_INSERT_LOCK "\ 47 | INSERT INTO statsrepo.lock VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16)" 48 | 49 | #define SQL_INSERT_BGWRITER "\ 50 | INSERT INTO statsrepo.bgwriter VALUES ($1, $2, $3, $4)" 51 | 52 | #define SQL_INSERT_REPLICATION "\ 53 | INSERT INTO statsrepo.replication VALUES \ 54 | ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21)" 55 | 56 | #define SQL_INSERT_REPLICATION_SLOTS "\ 57 | INSERT INTO statsrepo.replication_slots VALUES \ 58 | ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)" 59 | 60 | #define SQL_INSERT_STAT_REPLICATION_SLOTS "\ 61 | INSERT INTO statsrepo.stat_replication_slots VALUES \ 62 | ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)" 63 | 64 | #define SQL_INSERT_STAT_IO "\ 65 | INSERT INTO statsrepo.stat_io VALUES \ 66 | ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19)" 67 | 68 | #define SQL_INSERT_STAT_WAL "\ 69 | INSERT INTO statsrepo.stat_wal VALUES \ 70 | ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)" 71 | 72 | #define SQL_INSERT_XLOG "\ 73 | INSERT INTO statsrepo.xlog VALUES ($1, $2, $3)" 74 | 75 | #define SQL_INSERT_ARCHIVE "\ 76 | INSERT INTO statsrepo.archive VALUES ($1, $2, $3, $4, $5, $6, $7, $8)" 77 | 78 | #define SQL_INSERT_SETTING "\ 79 | INSERT INTO statsrepo.setting VALUES ($1, $2, $3, $4, $5)" 80 | 81 | #define SQL_INSERT_ROLE "\ 82 | INSERT INTO statsrepo.role VALUES ($1, $2, $3)" 83 | 84 | #define SQL_INSERT_CPU "\ 85 | INSERT INTO statsrepo.cpu VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)" 86 | 87 | #define SQL_INSERT_DEVICE "\ 88 | INSERT INTO statsrepo.device VALUES \ 89 | ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18)" 90 | 91 | #define SQL_INSERT_LOADAVG "\ 92 | INSERT INTO statsrepo.loadavg VALUES ($1, $2, $3, $4)" 93 | 94 | #define SQL_INSERT_MEMORY "\ 95 | INSERT INTO statsrepo.memory VALUES ($1, $2, $3, $4, $5, $6)" 96 | 97 | #define SQL_INSERT_PROFILE "\ 98 | INSERT INTO statsrepo.profile VALUES ($1, $2, $3, $4)" 99 | 100 | #define SQL_INSERT_RUSAGE "\ 101 | INSERT INTO statsrepo.rusage \ 102 | SELECT (($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20)::statsrepo.rusage).* \ 103 | FROM statsrepo.database d \ 104 | WHERE d.snapid = $1 AND d.dbid = $2" 105 | 106 | #define SQL_INSERT_HT_INFO "\ 107 | INSERT INTO statsrepo.ht_info VALUES ($1, $2, $3, $4, $5, $6, $7)" 108 | 109 | 110 | /* Definition of delimiter and null identifier for COPY command */ 111 | #define COPY_DELIMITER "\t" 112 | #define NULL_STR "null" 113 | 114 | #define SQL_COPY_SCHEMA "\ 115 | COPY statsrepo.schema FROM STDIN with(NULL '" NULL_STR "')" 116 | 117 | #define SQL_COPY_TABLE "\ 118 | COPY statsrepo.table FROM STDIN with(NULL '" NULL_STR "')" 119 | 120 | #define SQL_COPY_COLUMN "\ 121 | COPY statsrepo.column FROM STDIN with(NULL '" NULL_STR "')" 122 | 123 | #define SQL_COPY_INDEX "\ 124 | COPY statsrepo.index FROM STDIN with(NULL '" NULL_STR "')" 125 | 126 | #define SQL_COPY_INHERITS "\ 127 | COPY statsrepo.inherits FROM STDIN with(NULL '" NULL_STR "')" 128 | 129 | #define SQL_COPY_FUNCTION "\ 130 | COPY statsrepo.function FROM STDIN with(NULL '" NULL_STR "')" 131 | 132 | #define SQL_INSERT_ALERT "\ 133 | INSERT INTO statsrepo.alert_message VALUES ($1, $2)" 134 | 135 | #define SQL_INSERT_LOG "\ 136 | INSERT INTO statsrepo.log VALUES \ 137 | ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25, $26, $27)" 138 | 139 | #define SQL_UPDATE_SNAPSHOT "\ 140 | UPDATE \ 141 | statsrepo.snapshot \ 142 | SET \ 143 | exec_time = pg_catalog.age($2, $3), \ 144 | snapshot_increase_size = ((SELECT pg_catalog.sum(pg_catalog.pg_relation_size(oid)) FROM pg_class \ 145 | WHERE relnamespace = (SELECT oid FROM pg_namespace WHERE nspname = 'statsrepo')) - $4), \ 146 | xid_current = pg_catalog.pg_snapshot_xmax(pg_catalog.pg_current_snapshot()) \ 147 | WHERE \ 148 | snapid = $1" 149 | 150 | #define SQL_CREATE_SNAPSHOT_PARTITION "\ 151 | SELECT statsrepo.create_snapshot_partition($1)" 152 | 153 | #define SQL_CREATE_REPOLOG_PARTITION "\ 154 | SELECT statsrepo.create_repolog_partition($1)" 155 | 156 | #define SQL_INSERT_WAIT_SAMPLING_PROFILE "\ 157 | INSERT INTO statsrepo.wait_sampling VALUES ($1, $2, $3, $4, $5, $6, $7, $8)" 158 | 159 | #endif 160 | -------------------------------------------------------------------------------- /agent/common.h: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | * 3 | * common.h 4 | * 5 | * Copyright (c) 2009-2025, NIPPON TELEGRAPH AND TELEPHONE CORPORATION 6 | * 7 | *------------------------------------------------------------------------- 8 | */ 9 | 10 | #ifndef PG_STATSINFO_COMMON_H 11 | #define PG_STATSINFO_COMMON_H 12 | 13 | #include "catalog/pg_control.h" 14 | 15 | #ifndef WIN32 16 | #include "linux/version.h" 17 | #endif 18 | 19 | #define LINUX_VERSION_AT_LEAST(major, minor, patch) \ 20 | (LINUX_VERSION_CODE >= KERNEL_VERSION(major, minor, patch)) 21 | 22 | #define GLIBC_VERSION_AT_LEAST(major, minor) \ 23 | (__GLIBC__ > major || (__GLIBC__ == major && __GLIBC_MINOR__ >= minor)) 24 | 25 | #ifndef HAVE_SYNC_FILE_RANGE 26 | #if (LINUX_VERSION_AT_LEAST(2,6,17) && GLIBC_VERSION_AT_LEAST(2,6)) 27 | #define HAVE_SYNC_FILE_RANGE 28 | #endif 29 | #endif 30 | 31 | /* Error level */ 32 | #define DEBUG DEBUG2 33 | #define ALERT (PANIC + 1) 34 | #define DISABLE (PANIC + 2) 35 | 36 | /* guc parameter name prefix for the program */ 37 | #define GUC_PREFIX "pg_statsinfo" 38 | 39 | /* log message prefix for the program */ 40 | #define LOG_PREFIX "pg_statsinfo: " 41 | 42 | /* manual snapshot log message */ 43 | #define LOGMSG_SNAPSHOT LOG_PREFIX "snapshot requested" 44 | /* manual maintenance log message */ 45 | #define LOGMSG_MAINTENANCE LOG_PREFIX "maintenance requested" 46 | #define LOGMSG_RESTART LOG_PREFIX "restart requested" 47 | /* statistics log message */ 48 | #define LOGMSG_AUTOVACUUM_CANCEL_REQUEST LOG_PREFIX "autovacuum cancel request" 49 | 50 | /* exit code for pg_statsinfod */ 51 | #define STATSINFO_EXIT_SUCCESS 0x00 52 | #define STATSINFO_EXIT_FAILED 0xff 53 | 54 | /* lock file path */ 55 | #define TEMP_DIR "/run/pg_statsinfo" 56 | 57 | /* CRC calculation */ 58 | #include "port/pg_crc32c.h" 59 | #define INIT_CRC32 INIT_CRC32C 60 | #define COMP_CRC32 COMP_CRC32C 61 | #define FIN_CRC32 FIN_CRC32C 62 | #define EQ_CRC32 EQ_CRC32C 63 | typedef pg_crc32c pg_crc32; 64 | 65 | extern bool readControlFile(ControlFileData *ctrl, const char *pgdata); 66 | 67 | #endif /* PG_STATSINFO_COMMON_H */ 68 | -------------------------------------------------------------------------------- /agent/lib/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # pg_statsinfo: lib/Makefile 3 | # 4 | # Copyright (c) 2009-2025, NIPPON TELEGRAPH AND TELEPHONE CORPORATION 5 | # 6 | SRCS = \ 7 | libstatsinfo.c \ 8 | last_xact_activity.c \ 9 | wait_sampling.c \ 10 | pg_control.c \ 11 | port.c \ 12 | pgut/pgut-spi.c 13 | OBJS = $(SRCS:.c=.o) 14 | DATA_built = pg_statsinfo.sql 15 | DATA = uninstall_pg_statsinfo.sql 16 | MODULE_big = pg_statsinfo 17 | 18 | SHLIB_LINK = -Wl,--version-script=library.map 19 | 20 | ifndef USE_PGXS 21 | top_builddir = ../../../.. 22 | makefile_global = $(top_builddir)/src/Makefile.global 23 | ifeq "$(wildcard $(makefile_global))" "" 24 | USE_PGXS = 1 # use pgxs if not in contrib directory 25 | endif 26 | endif 27 | 28 | ifdef USE_PGXS 29 | PG_CONFIG = pg_config 30 | PGXS := $(shell $(PG_CONFIG) --pgxs) 31 | include $(PGXS) 32 | else 33 | subdir = contrib/$(MODULE_big) 34 | include $(makefile_global) 35 | include $(top_srcdir)/contrib/contrib-global.mk 36 | endif 37 | -------------------------------------------------------------------------------- /agent/lib/library.map: -------------------------------------------------------------------------------- 1 | { 2 | global: 3 | Pg_magic_func; 4 | pg_finfo_*; 5 | _PG_fini; 6 | _PG_init; 7 | StatsinfoLauncherMain; 8 | statsinfo_activity; 9 | statsinfo_cpustats; 10 | statsinfo_cpustats_noarg; 11 | statsinfo_devicestats; 12 | statsinfo_last_xact_activity; 13 | statsinfo_loadavg; 14 | statsinfo_long_xact; 15 | statsinfo_maintenance; 16 | statsinfo_memory; 17 | statsinfo_profile; 18 | statsinfo_cpuinfo; 19 | statsinfo_meminfo; 20 | statsinfo_rusage; 21 | statsinfo_rusage_reset; 22 | statsinfo_rusage_info; 23 | statsinfo_sample; 24 | statsinfo_sample_wait_sampling; 25 | statsinfo_sample_wait_sampling_reset; 26 | statsinfo_sample_wait_sampling_info; 27 | statsinfo_snapshot; 28 | statsinfo_start; 29 | statsinfo_stop; 30 | statsinfo_tablespaces; 31 | statsinfo_wait_sampling_profile; 32 | statsinfo_wait_sampling_reset_profile; 33 | local: *; 34 | }; 35 | -------------------------------------------------------------------------------- /agent/lib/libstatsinfo.h: -------------------------------------------------------------------------------- 1 | /* 2 | * lib/libstatsinfo.h 3 | * 4 | * Copyright (c) 2009-2025, NIPPON TELEGRAPH AND TELEPHONE CORPORATION 5 | */ 6 | 7 | #ifndef LIBSTATSINFO_H 8 | #define LIBSTATSINFO_H 9 | 10 | #include "postgres.h" 11 | 12 | #ifdef WIN32 13 | extern ssize_t readlink(const char *path, char *target, size_t size); 14 | extern pid_t getppid(void); 15 | #endif 16 | 17 | extern pid_t forkexec(const char *cmd, int *outStdin); 18 | extern bool get_diskspace(const char *path, int64 *total, int64 *avail); 19 | 20 | #endif /* LIBSTATSINFO_H */ 21 | -------------------------------------------------------------------------------- /agent/lib/pg_control.c: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | * 3 | * pg_control.c 4 | * 5 | * Copyright (c) 2009-2025, NIPPON TELEGRAPH AND TELEPHONE CORPORATION 6 | * 7 | *------------------------------------------------------------------------- 8 | */ 9 | 10 | #include "c.h" 11 | #include "../common.h" 12 | 13 | #include 14 | #include 15 | 16 | bool 17 | readControlFile(ControlFileData *ctrl, const char *pgdata) 18 | { 19 | char path[MAXPGPATH]; 20 | int fd; 21 | pg_crc32 crc; 22 | 23 | snprintf(path, MAXPGPATH, "%s/global/pg_control", pgdata); 24 | 25 | if ((fd = open(path, O_RDONLY | PG_BINARY, 0)) == -1) 26 | return false; 27 | 28 | if (read(fd, ctrl, sizeof(ControlFileData)) != sizeof(ControlFileData)) 29 | return false; 30 | close(fd); 31 | 32 | /* Check the CRC. */ 33 | INIT_CRC32(crc); 34 | COMP_CRC32(crc, 35 | (char *) ctrl, 36 | offsetof(ControlFileData, crc)); 37 | FIN_CRC32(crc); 38 | 39 | if (!EQ_CRC32(crc, ctrl->crc)) 40 | return false; 41 | 42 | return true; 43 | } 44 | 45 | #ifdef FRONTEND 46 | /* 47 | * copied from pg_crc.c. 48 | * 49 | * This table is based on the polynomial 50 | * x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1. 51 | * (This is the same polynomial used in Ethernet checksums, for instance.) 52 | */ 53 | const uint32 pg_crc32_table[256] = { 54 | 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 55 | 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, 56 | 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 57 | 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 58 | 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 59 | 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 60 | 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 61 | 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 62 | 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 63 | 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 64 | 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 65 | 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 66 | 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 67 | 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, 68 | 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 69 | 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 70 | 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 71 | 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, 72 | 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 73 | 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, 74 | 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 75 | 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 76 | 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 77 | 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 78 | 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 79 | 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 80 | 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 81 | 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 82 | 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 83 | 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, 84 | 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 85 | 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, 86 | 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 87 | 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 88 | 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 89 | 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 90 | 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 91 | 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, 92 | 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 93 | 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 94 | 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 95 | 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, 96 | 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 97 | 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, 98 | 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 99 | 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 100 | 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 101 | 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, 102 | 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 103 | 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, 104 | 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 105 | 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 106 | 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 107 | 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, 108 | 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 109 | 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, 110 | 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 111 | 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 112 | 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 113 | 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, 114 | 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 115 | 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, 116 | 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 117 | 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D 118 | }; 119 | #endif 120 | -------------------------------------------------------------------------------- /agent/lib/pg_statsinfo.sql.in: -------------------------------------------------------------------------------- 1 | /* 2 | * lib/pg_statsinfo.sql.in 3 | * 4 | * Copyright (c) 2009-2025, NIPPON TELEGRAPH AND TELEPHONE CORPORATION 5 | */ 6 | 7 | -- Adjust this setting to control where the objects get created. 8 | SET search_path = public; 9 | 10 | BEGIN; 11 | 12 | SET LOCAL client_min_messages = WARNING; 13 | 14 | CREATE SCHEMA statsinfo; 15 | REVOKE ALL ON SCHEMA statsinfo FROM PUBLIC; 16 | 17 | -- 18 | -- statsinfo.array_agg() 19 | -- 20 | CREATE AGGREGATE statsinfo.array_agg(anycompatible) 21 | ( 22 | SFUNC=array_append, 23 | STYPE=anycompatiblearray, 24 | INITCOND='{}' 25 | ); 26 | 27 | -- 28 | -- statsinfo.sample() 29 | -- 30 | CREATE FUNCTION statsinfo.sample() RETURNS void 31 | AS 'MODULE_PATHNAME', 'statsinfo_sample' 32 | LANGUAGE C STRICT; 33 | 34 | -- 35 | -- statsinfo.sample_wait_sampling() 36 | -- 37 | CREATE FUNCTION statsinfo.sample_wait_sampling() RETURNS void 38 | AS 'MODULE_PATHNAME', 'statsinfo_sample_wait_sampling' 39 | LANGUAGE C STRICT; 40 | 41 | -- 42 | -- statsinfo.activity() 43 | -- 44 | CREATE FUNCTION statsinfo.activity( 45 | OUT idle integer, 46 | OUT idle_in_xact integer, 47 | OUT waiting integer, 48 | OUT running integer, 49 | OUT backends integer) 50 | AS 'MODULE_PATHNAME', 'statsinfo_activity' 51 | LANGUAGE C STRICT; 52 | 53 | -- 54 | -- statsinfo.long_xact() 55 | -- 56 | CREATE FUNCTION statsinfo.long_xact( 57 | OUT client text, 58 | OUT pid integer, 59 | OUT start timestamptz, 60 | OUT duration float8, 61 | OUT query text) 62 | RETURNS SETOF record 63 | AS 'MODULE_PATHNAME', 'statsinfo_long_xact' 64 | LANGUAGE C STRICT; 65 | 66 | -- 67 | -- statsinfo.snapshot() 68 | -- 69 | CREATE FUNCTION statsinfo.snapshot(comment text) RETURNS void 70 | AS 'MODULE_PATHNAME', 'statsinfo_snapshot' 71 | LANGUAGE C; 72 | 73 | CREATE FUNCTION statsinfo.snapshot() RETURNS void 74 | AS 'MODULE_PATHNAME', 'statsinfo_snapshot' 75 | LANGUAGE C; 76 | 77 | -- 78 | -- statsinfo.maintenance() 79 | -- 80 | CREATE FUNCTION statsinfo.maintenance(repository_keep_period timestamptz) RETURNS void 81 | AS 'MODULE_PATHNAME', 'statsinfo_maintenance' 82 | LANGUAGE C; 83 | 84 | -- 85 | -- statsinfo.tablespaces() 86 | -- 87 | CREATE FUNCTION statsinfo.tablespaces( 88 | OUT oid oid, 89 | OUT name text, 90 | OUT location text, 91 | OUT device text, 92 | OUT avail bigint, 93 | OUT total bigint, 94 | OUT spcoptions text[]) 95 | RETURNS SETOF record 96 | AS 'MODULE_PATHNAME', 'statsinfo_tablespaces' 97 | LANGUAGE C STRICT; 98 | 99 | CREATE VIEW statsinfo.tablespaces AS 100 | SELECT * FROM statsinfo.tablespaces(); 101 | 102 | -- 103 | -- statsinfo.start() 104 | -- 105 | CREATE FUNCTION statsinfo.start(timeout integer) RETURNS void 106 | AS 'MODULE_PATHNAME', 'statsinfo_start' 107 | LANGUAGE C; 108 | 109 | -- 110 | -- statsinfo.stop() 111 | -- 112 | CREATE FUNCTION statsinfo.stop(timeout integer) RETURNS void 113 | AS 'MODULE_PATHNAME', 'statsinfo_stop' 114 | LANGUAGE C; 115 | 116 | -- 117 | -- statsinfo.cpustats() 118 | -- 119 | CREATE TYPE statsinfo.cpustats_type AS 120 | ( 121 | cpu_user bigint, 122 | cpu_system bigint, 123 | cpu_idle bigint, 124 | cpu_iowait bigint 125 | ); 126 | 127 | CREATE FUNCTION statsinfo.cpustats 128 | ( 129 | IN prev_cpustats statsinfo.cpustats_type, 130 | OUT cpu_id text, 131 | OUT cpu_user bigint, 132 | OUT cpu_system bigint, 133 | OUT cpu_idle bigint, 134 | OUT cpu_iowait bigint, 135 | OUT overflow_user smallint, 136 | OUT overflow_system smallint, 137 | OUT overflow_idle smallint, 138 | OUT overflow_iowait smallint 139 | ) 140 | RETURNS SETOF record 141 | AS 'MODULE_PATHNAME', 'statsinfo_cpustats' 142 | LANGUAGE C STRICT; 143 | 144 | CREATE FUNCTION statsinfo.cpustats 145 | ( 146 | OUT cpu_id text, 147 | OUT cpu_user bigint, 148 | OUT cpu_system bigint, 149 | OUT cpu_idle bigint, 150 | OUT cpu_iowait bigint, 151 | OUT overflow_user smallint, 152 | OUT overflow_system smallint, 153 | OUT overflow_idle smallint, 154 | OUT overflow_iowait smallint 155 | ) 156 | RETURNS SETOF record 157 | AS 'MODULE_PATHNAME', 'statsinfo_cpustats_noarg' 158 | LANGUAGE C STRICT; 159 | 160 | -- 161 | -- statsinfo.devicestats() 162 | -- 163 | CREATE FUNCTION statsinfo.devicestats 164 | ( 165 | OUT device_major text, 166 | OUT device_minor text, 167 | OUT device_name text, 168 | OUT device_readsector bigint, 169 | OUT device_readtime bigint, 170 | OUT device_writesector bigint, 171 | OUT device_writetime bigint, 172 | OUT device_ioqueue bigint, 173 | OUT device_iototaltime bigint, 174 | OUT device_rsps_max float8, 175 | OUT device_wsps_max float8, 176 | OUT overflow_drs smallint, 177 | OUT overflow_drt smallint, 178 | OUT overflow_dws smallint, 179 | OUT overflow_dwt smallint, 180 | OUT overflow_dit smallint, 181 | OUT device_tblspaces name[] 182 | ) 183 | RETURNS SETOF record 184 | AS 'MODULE_PATHNAME', 'statsinfo_devicestats' 185 | LANGUAGE C; 186 | 187 | -- 188 | -- statsinfo.loadavg() 189 | -- 190 | CREATE FUNCTION statsinfo.loadavg 191 | ( 192 | OUT loadavg1 real, 193 | OUT loadavg5 real, 194 | OUT loadavg15 real 195 | ) 196 | RETURNS SETOF record 197 | AS 'MODULE_PATHNAME', 'statsinfo_loadavg' 198 | LANGUAGE C; 199 | 200 | -- 201 | -- statsinfo.memory() 202 | -- 203 | CREATE FUNCTION statsinfo.memory 204 | ( 205 | OUT memfree bigint, 206 | OUT buffers bigint, 207 | OUT cached bigint, 208 | OUT swap bigint, 209 | OUT dirty bigint 210 | ) 211 | RETURNS SETOF record 212 | AS 'MODULE_PATHNAME', 'statsinfo_memory' 213 | LANGUAGE C; 214 | 215 | -- 216 | -- statsinfo.profile() 217 | -- 218 | CREATE FUNCTION statsinfo.profile 219 | ( 220 | OUT processing text, 221 | OUT execute bigint, 222 | OUT total_exec_time double precision 223 | ) 224 | RETURNS SETOF record 225 | AS 'MODULE_PATHNAME', 'statsinfo_profile' 226 | LANGUAGE C STRICT; 227 | 228 | -- 229 | -- statsinfo.last_xact_activity() 230 | -- 231 | CREATE FUNCTION statsinfo.last_xact_activity 232 | ( 233 | OUT pid int, 234 | OUT xid xid, 235 | OUT in_xact bool, 236 | OUT queries text 237 | ) 238 | RETURNS SETOF record 239 | AS 'MODULE_PATHNAME', 'statsinfo_last_xact_activity' 240 | LANGUAGE C STRICT; 241 | 242 | CREATE FUNCTION statsinfo.rusage( 243 | OUT queryid bigint, 244 | OUT top bool, 245 | OUT userid oid, 246 | OUT dbid oid, 247 | /* planning time */ 248 | OUT plan_reads bigint, /* total reads, in bytes */ 249 | OUT plan_writes bigint, /* total writes, in bytes */ 250 | OUT plan_user_time double precision, /* total user CPU time used */ 251 | OUT plan_system_time double precision, /* total system CPU time used */ 252 | OUT plan_minflts bigint, /* total page reclaims (soft page faults) */ 253 | OUT plan_majflts bigint, /* total page faults (hard page faults) */ 254 | OUT plan_nvcsws bigint, /* total voluntary context switches */ 255 | OUT plan_nivcsws bigint, /* total involuntary context switches */ 256 | /* execution time */ 257 | OUT exec_reads bigint, /* total reads, in bytes */ 258 | OUT exec_writes bigint, /* total writes, in bytes */ 259 | OUT exec_user_time double precision, /* total user CPU time used */ 260 | OUT exec_system_time double precision, /* total system CPU time used */ 261 | OUT exec_minflts bigint, /* total page reclaims (soft page faults) */ 262 | OUT exec_majflts bigint, /* total page faults (hard page faults) */ 263 | OUT exec_nvcsws bigint, /* total voluntary context switches */ 264 | OUT exec_nivcsws bigint /* total involuntary context switches */ 265 | ) 266 | RETURNS SETOF record 267 | AS 'MODULE_PATHNAME', 'statsinfo_rusage' 268 | LANGUAGE C STRICT; 269 | 270 | CREATE FUNCTION statsinfo.rusage_info( 271 | OUT dealloc bigint, 272 | OUT stats_reset timestamp with time zone 273 | ) 274 | RETURNS record 275 | AS 'MODULE_PATHNAME', 'statsinfo_rusage_info' 276 | LANGUAGE C STRICT; 277 | 278 | CREATE FUNCTION statsinfo.rusage_reset() 279 | RETURNS void 280 | AS 'MODULE_PATHNAME', 'statsinfo_rusage_reset' 281 | LANGUAGE C STRICT; 282 | 283 | CREATE FUNCTION statsinfo.wait_sampling_profile ( 284 | OUT dbid oid, 285 | OUT userid oid, 286 | OUT queryid bigint, 287 | OUT backend_type text, 288 | OUT event_type text, 289 | OUT event text, 290 | OUT count bigint 291 | ) 292 | RETURNS SETOF record 293 | AS 'MODULE_PATHNAME', 'statsinfo_wait_sampling_profile' 294 | LANGUAGE C VOLATILE STRICT; 295 | 296 | CREATE FUNCTION statsinfo.sample_wait_sampling_reset() RETURNS void 297 | AS 'MODULE_PATHNAME', 'statsinfo_sample_wait_sampling_reset' 298 | LANGUAGE C VOLATILE STRICT; 299 | 300 | --- Define statsinfo.sample_wait_sampling_info 301 | CREATE FUNCTION statsinfo.sample_wait_sampling_info( 302 | OUT dealloc bigint, 303 | OUT stats_reset timestamp with time zone 304 | ) 305 | RETURNS record 306 | AS 'MODULE_PATHNAME', 'statsinfo_sample_wait_sampling_info' 307 | LANGUAGE C STRICT VOLATILE PARALLEL SAFE; 308 | 309 | CREATE VIEW statsinfo.sample_wait_sampling_info AS 310 | SELECT * FROM statsinfo.sample_wait_sampling_info(); 311 | 312 | -- 313 | -- statsinfo.cpuinfo() 314 | -- 315 | CREATE FUNCTION statsinfo.cpuinfo 316 | ( 317 | OUT vendor_id text, 318 | OUT model_name text, 319 | OUT cpu_mhz real, 320 | OUT processors int, 321 | OUT threads_per_core int, 322 | OUT cores_per_socket int, 323 | OUT sockets int 324 | ) 325 | RETURNS record 326 | AS 'MODULE_PATHNAME', 'statsinfo_cpuinfo' 327 | LANGUAGE C STRICT; 328 | 329 | -- 330 | -- statsinfo.meminfo() 331 | -- 332 | CREATE FUNCTION statsinfo.meminfo 333 | ( 334 | OUT mem_total bigint 335 | ) RETURNS bigint 336 | AS 'MODULE_PATHNAME', 'statsinfo_meminfo' 337 | LANGUAGE C STRICT; 338 | 339 | COMMIT; 340 | -------------------------------------------------------------------------------- /agent/lib/pgut/pgut-be.h: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | * 3 | * pgut-be.h 4 | * 5 | * Copyright (c) 2009-2025, NIPPON TELEGRAPH AND TELEPHONE CORPORATION 6 | * 7 | *------------------------------------------------------------------------- 8 | */ 9 | 10 | #ifndef PGUT_BE_H 11 | #define PGUT_BE_H 12 | 13 | #include "fmgr.h" 14 | #include "utils/tuplestore.h" 15 | 16 | #ifndef WIN32 17 | 18 | #define PGUT_EXPORT 19 | 20 | #else 21 | 22 | #define PGUT_EXPORT __declspec(dllexport) 23 | 24 | /* 25 | * PG_MODULE_MAGIC and PG_FUNCTION_INFO_V1 macros seems to be broken. 26 | * It uses PGDLLIMPORT, but those objects are not imported from postgres 27 | * and exported from the user module. So, it should be always dllexported. 28 | */ 29 | 30 | #undef PG_MODULE_MAGIC 31 | #define PG_MODULE_MAGIC \ 32 | extern PGUT_EXPORT const Pg_magic_struct *PG_MAGIC_FUNCTION_NAME(void); \ 33 | const Pg_magic_struct * \ 34 | PG_MAGIC_FUNCTION_NAME(void) \ 35 | { \ 36 | static const Pg_magic_struct Pg_magic_data = PG_MODULE_MAGIC_DATA; \ 37 | return &Pg_magic_data; \ 38 | } \ 39 | extern int no_such_variable 40 | 41 | #undef PG_FUNCTION_INFO_V1 42 | #define PG_FUNCTION_INFO_V1(funcname) \ 43 | extern PGUT_EXPORT const Pg_finfo_record * CppConcat(pg_finfo_,funcname)(void); \ 44 | const Pg_finfo_record * \ 45 | CppConcat(pg_finfo_,funcname) (void) \ 46 | { \ 47 | static const Pg_finfo_record my_finfo = { 1 }; \ 48 | return &my_finfo; \ 49 | } \ 50 | extern int no_such_variable 51 | 52 | #endif 53 | 54 | #define GetConfigOption(name, restrict_superuser) \ 55 | GetConfigOption((name), false, (restrict_superuser)) 56 | 57 | #endif /* PGUT_BE_H */ 58 | -------------------------------------------------------------------------------- /agent/lib/pgut/pgut-spi.c: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | * 3 | * pgut-spi.c 4 | * 5 | * Copyright (c) 2009-2025, NIPPON TELEGRAPH AND TELEPHONE CORPORATION 6 | * 7 | *------------------------------------------------------------------------- 8 | */ 9 | 10 | #include "postgres.h" 11 | #include "pgut-spi.h" 12 | 13 | #define EXEC_FAILED(ret, expected) \ 14 | (((expected) > 0 && (ret) != (expected)) || (ret) < 0) 15 | 16 | /* simple execute */ 17 | void 18 | execute(int expected, const char *sql) 19 | { 20 | int ret = SPI_execute(sql, false, 0); 21 | if EXEC_FAILED(ret, expected) 22 | elog(ERROR, "query failed: (sql=%s, code=%d, expected=%d)", sql, ret, expected); 23 | } 24 | -------------------------------------------------------------------------------- /agent/lib/pgut/pgut-spi.h: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | * 3 | * pgut-spi.h 4 | * 5 | * Copyright (c) 2009-2025, NIPPON TELEGRAPH AND TELEPHONE CORPORATION 6 | * 7 | *------------------------------------------------------------------------- 8 | */ 9 | 10 | #ifndef PGUT_SPI_H 11 | #define PGUT_SPI_H 12 | 13 | #include "executor/spi.h" 14 | 15 | extern void execute(int expected, const char *sql); 16 | 17 | #endif /* PGUT_SPI_H */ 18 | -------------------------------------------------------------------------------- /agent/lib/rusage.h: -------------------------------------------------------------------------------- 1 | /* 2 | * lib/rusage.h 3 | * 4 | * Copyright (c) 2009-2025, NIPPON TELEGRAPH AND TELEPHONE CORPORATION 5 | */ 6 | 7 | 8 | typedef enum 9 | { 10 | STATSINFO_RUSAGE_TRACK_NONE, /* track no statements */ 11 | STATSINFO_RUSAGE_TRACK_TOP, /* only top level statements */ 12 | STATSINFO_RUSAGE_TRACK_ALL /* all statements, including nested ones */ 13 | } STATSINFO_RUSAGE_TrackLevel; 14 | 15 | static const struct config_enum_entry rusage_track_options[] = 16 | { 17 | {"none", STATSINFO_RUSAGE_TRACK_NONE, false}, 18 | {"top", STATSINFO_RUSAGE_TRACK_TOP, false}, 19 | {"all", STATSINFO_RUSAGE_TRACK_ALL, false}, 20 | {NULL, 0, false} 21 | }; 22 | -------------------------------------------------------------------------------- /agent/lib/uninstall_pg_statsinfo.sql: -------------------------------------------------------------------------------- 1 | /* 2 | * lib/uninstall_pg_statsinfo.sql 3 | * 4 | * Copyright (c) 2009-2025, NIPPON TELEGRAPH AND TELEPHONE CORPORATION 5 | */ 6 | 7 | DROP SCHEMA IF EXISTS statsinfo CASCADE; 8 | -------------------------------------------------------------------------------- /agent/lib/wait_sampling.c: -------------------------------------------------------------------------------- 1 | /* 2 | * lib/wait_sampling.c 3 | * Collect statistics of wait sample. 4 | * 5 | * Copyright (c) 2009-2025, NIPPON TELEGRAPH AND TELEPHONE CORPORATION 6 | */ 7 | 8 | #include "postgres.h" 9 | 10 | #include 11 | 12 | 13 | #include "storage/proc.h" 14 | #include "funcapi.h" 15 | #include "miscadmin.h" 16 | #include "storage/ipc.h" 17 | #include "utils/builtins.h" 18 | #include "pgstat.h" 19 | #include "utils/memutils.h" 20 | 21 | #include "../common.h" 22 | #include "pgut/pgut-be.h" 23 | 24 | #include "wait_sampling.h" 25 | #include "optimizer/planner.h" 26 | #include "access/twophase.h" 27 | #include "utils/datetime.h" 28 | 29 | #ifndef PG_MODULE_MAGIC 30 | PG_MODULE_MAGIC; 31 | #endif 32 | 33 | /* Backend local variables */ 34 | wait_samplingSharedState *wait_sampling = NULL; 35 | extern bool wait_sampling_queries; 36 | extern int wait_sampling_max; 37 | extern bool wait_sampling_save; 38 | extern HTAB *wait_sampling_hash; 39 | 40 | /* Module callbacks */ 41 | void init_wait_sampling(void); 42 | void fini_wait_sampling(void); 43 | 44 | /* Internal functions */ 45 | static void wait_sampling_shmem_shutdown(int code, Datum arg); 46 | void wait_sampling_shmem_startup(void); 47 | static void attatch_shmem(void); 48 | static Size wait_sampling_memsize(void); 49 | extern uint32 wait_sampling_hash_fn(const void *key, Size keysize); 50 | extern int wait_sampling_match_fn(const void *key1, const void *key2, Size keysize); 51 | extern void wait_sampling_entry_alloc(wait_samplingEntry *item, bool direct); 52 | // static void errout(char* format, ...) { 53 | // va_list list; 54 | // 55 | // FILE *f = fopen("/tmp/errout", "a"); 56 | // if (f == NULL) return; 57 | // 58 | // va_start(list, format); 59 | // vfprintf(f, format, list); 60 | // va_end(list); 61 | // fclose(f); 62 | // } 63 | 64 | /* 65 | * Module load callbacks 66 | */ 67 | void 68 | init_wait_sampling(void) 69 | { 70 | RequestAddinShmemSpace(wait_sampling_memsize()); 71 | RequestNamedLWLockTranche("sample_wait_sampling", 1); 72 | } 73 | 74 | /* 75 | * Module unload callback 76 | */ 77 | void 78 | fini_wait_sampling(void) 79 | { 80 | /* do nothing */ 81 | } 82 | 83 | /* 84 | * wait_sampling_shmem_startup() - 85 | * 86 | * Allocate or attach shared memory, and set up a process-exit hook function. 87 | */ 88 | void 89 | wait_sampling_shmem_startup(void) 90 | { 91 | attatch_shmem(); 92 | 93 | /* 94 | * If we're in the postmaster (or a standalone backend...), set up a shmem 95 | * exit hook to dump the statistics to disk. 96 | */ 97 | if (!IsUnderPostmaster) 98 | on_shmem_exit(wait_sampling_shmem_shutdown, 0); 99 | } 100 | 101 | /* 102 | * wait_sampling_shmem_shutdown() - 103 | * Dump statistics into file. 104 | */ 105 | static void 106 | wait_sampling_shmem_shutdown(int code, Datum arg) 107 | { 108 | FILE *file; 109 | HASH_SEQ_STATUS hash_seq; 110 | int32 num_entries; 111 | wait_samplingEntry *entry; 112 | 113 | /* Don't try to dump during a crash. */ 114 | if (code) 115 | return; 116 | 117 | /* Safety check ... shouldn't get here unless shmem is set up. */ 118 | if (!wait_sampling || !wait_sampling_hash) 119 | return; 120 | 121 | /* Don't dump if told not to. */ 122 | if (!wait_sampling_save) 123 | return; 124 | 125 | file = AllocateFile(STATSINFO_WS_DUMP_FILE ".tmp", PG_BINARY_W); 126 | if (file == NULL) 127 | goto error; 128 | 129 | if (fwrite(&STATSINFO_WS_FILE_HEADER, sizeof(uint32), 1, file) != 1) 130 | goto error; 131 | 132 | num_entries = hash_get_num_entries(wait_sampling_hash); 133 | if (fwrite(&num_entries, sizeof(int32), 1, file) != 1) 134 | goto error; 135 | 136 | /* Serializing to disk. */ 137 | hash_seq_init(&hash_seq, wait_sampling_hash); 138 | while ((entry = hash_seq_search(&hash_seq)) != NULL) 139 | { 140 | if (fwrite(entry, sizeof(wait_samplingEntry), 1, file) != 1) 141 | { 142 | /* note: we assume hash_seq_term won't change errno */ 143 | hash_seq_term(&hash_seq); 144 | goto error; 145 | } 146 | } 147 | 148 | /* Dump global statistics */ 149 | if (fwrite(&wait_sampling->stats, sizeof(wait_samplingGlobalStats), 1, file) != 1) 150 | goto error; 151 | 152 | if (FreeFile(file)) 153 | { 154 | file = NULL; 155 | goto error; 156 | } 157 | 158 | /* Rename. If failed, a LOG message would be recorded. */ 159 | (void) durable_rename(STATSINFO_WS_DUMP_FILE ".tmp", STATSINFO_WS_DUMP_FILE, LOG); 160 | 161 | return; 162 | 163 | error: 164 | ereport(LOG, 165 | (errcode_for_file_access(), 166 | errmsg("could not write pg_statsinfo wait sampling file \"%s\": %m", 167 | STATSINFO_WS_DUMP_FILE ".tmp"))); 168 | 169 | if (file) 170 | FreeFile(file); 171 | unlink(STATSINFO_WS_DUMP_FILE ".tmp"); 172 | 173 | } 174 | 175 | static void 176 | attatch_shmem(void) 177 | { 178 | FILE *file = NULL; 179 | bool found; 180 | HASHCTL info; 181 | uint32 header; 182 | int32 num; 183 | int i; 184 | 185 | /* reset in case this is a restart within the postmaster */ 186 | wait_sampling = NULL; 187 | wait_sampling_hash = NULL; 188 | 189 | /* 190 | * Create or attach to the shared memory state, including hash table 191 | */ 192 | LWLockAcquire(AddinShmemInitLock, LW_EXCLUSIVE); 193 | 194 | wait_sampling = ShmemInitStruct("sample_wait_sampling", sizeof(wait_samplingSharedState), &found); 195 | 196 | if (!found) 197 | { 198 | /* First time through ... */ 199 | wait_sampling->lock = &(GetNamedLWLockTranche("sample_wait_sampling"))->lock; 200 | SpinLockInit(&wait_sampling->mutex); 201 | wait_sampling->stats.dealloc = 0; 202 | wait_sampling->stats.stats_reset = GetCurrentTimestamp(); 203 | } 204 | 205 | info.keysize = sizeof(wait_samplingHashKey); 206 | info.entrysize = sizeof(wait_samplingEntry); 207 | info.hash = wait_sampling_hash_fn; 208 | info.match = wait_sampling_match_fn; 209 | 210 | wait_sampling_hash = ShmemInitHash("wait sampling hash", 211 | wait_sampling_max, wait_sampling_max, 212 | &info, 213 | HASH_FUNCTION | HASH_ELEM | HASH_COMPARE); 214 | 215 | LWLockRelease(AddinShmemInitLock); 216 | 217 | /* 218 | * Done if some other process already completed our initialization. 219 | */ 220 | if (found) 221 | return; 222 | 223 | if (!wait_sampling_save) 224 | return; 225 | 226 | file = AllocateFile(STATSINFO_WS_DUMP_FILE, PG_BINARY_R); 227 | if (file == NULL) 228 | { 229 | if (errno != ENOENT) 230 | goto error; 231 | return; 232 | } 233 | 234 | if (fread(&header, sizeof(uint32), 1, file) != 1) 235 | goto error; 236 | 237 | if (header != STATSINFO_WS_FILE_HEADER) 238 | goto error; 239 | 240 | if (fread(&num, sizeof(int32), 1, file) != 1) 241 | goto error; 242 | 243 | 244 | /* 245 | * NOTE: read and store the old stats to hash-table. 246 | * It might be better to check wait_sampling_max and num(old stats number) before 247 | * issue entry_alloc. Because if num >> wait_sampling_max (change param between 248 | * PostgreSQL stop and start), it should cause high frequency dealloc()s. 249 | * TODO: optimization to avoid the high-frequency dealloc()s. 250 | */ 251 | for (i = 0; i < num; i++) 252 | { 253 | wait_samplingEntry temp; 254 | 255 | if (fread(&temp, sizeof(wait_samplingEntry), 1, file) != 1) 256 | goto error; 257 | 258 | /* enter this item to hash-table directly */ 259 | wait_sampling_entry_alloc(&temp, true); 260 | } 261 | 262 | /* Read global statistics. */ 263 | if (fread(&wait_sampling->stats, sizeof(wait_samplingGlobalStats), 1, file) != 1) 264 | goto error; 265 | 266 | FreeFile(file); 267 | 268 | unlink(STATSINFO_WS_DUMP_FILE); 269 | 270 | return; 271 | 272 | error: 273 | ereport(LOG, 274 | (errcode_for_file_access(), 275 | errmsg("could not read pg_statsinfo wait sampling stat file \"%s\": %m", 276 | STATSINFO_WS_DUMP_FILE))); 277 | 278 | if (file) 279 | FreeFile(file); 280 | /* delete bogus file, don't care of errors in this case */ 281 | unlink(STATSINFO_WS_DUMP_FILE); 282 | 283 | } 284 | 285 | /* 286 | * Estimate shared memory space needed. 287 | */ 288 | static Size 289 | wait_sampling_memsize(void) 290 | { 291 | Size size; 292 | 293 | size = MAXALIGN(sizeof(wait_samplingSharedState)); 294 | size = add_size(size, hash_estimate_size(wait_sampling_max, sizeof(wait_samplingEntry))); 295 | size = add_size(size, hash_estimate_size(wait_sampling_max, sizeof(wait_samplingSubEntry))); 296 | 297 | return size; 298 | } 299 | -------------------------------------------------------------------------------- /agent/lib/wait_sampling.h: -------------------------------------------------------------------------------- 1 | /* 2 | * lib/wait_sampling.h 3 | * 4 | * Copyright (c) 2009-2025, NIPPON TELEGRAPH AND TELEPHONE CORPORATION 5 | */ 6 | 7 | /* 8 | * For hash table dealloc factors. (same as pg_stat_statements) 9 | * TODO: should share for wait-sampling-hash-table 10 | */ 11 | #define STATSINFO_USAGE_INCREASE (1.0) 12 | #define STATSINFO_USAGE_DECREASE_FACTOR (0.99) 13 | #define STATSINFO_USAGE_DEALLOC_PERCENT 5 14 | #define STATSINFO_USAGE_INIT (1.0) 15 | 16 | /* Location of permanent stats file (valid when database is shut down) */ 17 | #define STATSINFO_WS_DUMP_FILE PGSTAT_STAT_PERMANENT_DIRECTORY "/pg_statsinfo_ws.stat" 18 | 19 | /* Magic number identifying the stats file format */ 20 | static const uint32 STATSINFO_WS_FILE_HEADER = 0x20210930; 21 | 22 | typedef struct 23 | { 24 | Oid userid; /* user OID */ 25 | Oid dbid; /* database OID */ 26 | uint64 queryid; /* query identifier */ 27 | BackendType backend_type; /* Type of backends */ 28 | uint32 wait_event_info; /* Wait sampling information */ 29 | } wait_samplingHashKey; 30 | 31 | /* wait sampling counters. */ 32 | typedef struct wait_samplingCounters 33 | { 34 | double usage; /* usage factor */ 35 | uint64 count; /* number of samples */ 36 | } wait_samplingCounters; 37 | 38 | /* wait sampling entry per database (same as pg_stat_statements) */ 39 | typedef struct wait_samplingEntry 40 | { 41 | wait_samplingHashKey key; /* hash key of entry - MUST BE FIRST */ 42 | wait_samplingCounters counters; /* statistics for this event */ 43 | slock_t mutex; /* protects the counters only */ 44 | } wait_samplingEntry; 45 | 46 | typedef struct 47 | { 48 | Oid userid; /* user OID */ 49 | Oid dbid; /* database OID */ 50 | uint64 queryid; /* query identifier */ 51 | } wait_samplingSubHashKey; 52 | 53 | typedef struct wait_samplingSubEntry 54 | { 55 | wait_samplingSubHashKey key; /* hash key of entry - MUST BE FIRST */ 56 | double usage; /* usage factor */ 57 | } wait_samplingSubEntry; 58 | 59 | /* 60 | * Global statistics for sample_wait_sampling 61 | */ 62 | typedef struct wait_samplingGlobalStats 63 | { 64 | int64 dealloc; /* # of times entries were deallocated */ 65 | TimestampTz stats_reset; /* timestamp with all stats reset */ 66 | } wait_samplingGlobalStats; 67 | 68 | typedef struct wait_samplingSharedState 69 | { 70 | LWLock *lock; /* protects hashtable search/modification */ 71 | slock_t mutex; /* protects following fields only: */ 72 | wait_samplingGlobalStats stats; /* global statistics for wait_sampling */ 73 | } wait_samplingSharedState; 74 | -------------------------------------------------------------------------------- /doc/files/pg_statsinfo_v17_report_infomation.xls: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ossc-db/pg_statsinfo/9d484507db17c4cf66d82b2cf0e6609a6a339638/doc/files/pg_statsinfo_v17_report_infomation.xls -------------------------------------------------------------------------------- /doc/files/pg_statsinfo_v17_repository_infomation.xls: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ossc-db/pg_statsinfo/9d484507db17c4cf66d82b2cf0e6609a6a339638/doc/files/pg_statsinfo_v17_repository_infomation.xls -------------------------------------------------------------------------------- /doc/image/pg_statsinfo-ja.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ossc-db/pg_statsinfo/9d484507db17c4cf66d82b2cf0e6609a6a339638/doc/image/pg_statsinfo-ja.png -------------------------------------------------------------------------------- /doc/image/pg_statsinfo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ossc-db/pg_statsinfo/9d484507db17c4cf66d82b2cf0e6609a6a339638/doc/image/pg_statsinfo.png -------------------------------------------------------------------------------- /doc/image/system_structure-ja.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ossc-db/pg_statsinfo/9d484507db17c4cf66d82b2cf0e6609a6a339638/doc/image/system_structure-ja.png -------------------------------------------------------------------------------- /doc/image/system_structure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ossc-db/pg_statsinfo/9d484507db17c4cf66d82b2cf0e6609a6a339638/doc/image/system_structure.png -------------------------------------------------------------------------------- /reporter/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # pg_statsinfo: Makefile 3 | # 4 | # Copyright (c) 2009-2025, NIPPON TELEGRAPH AND TELEPHONE CORPORATION 5 | # 6 | SRCS = \ 7 | pg_statsinfo.c \ 8 | snapshot.c \ 9 | report.c \ 10 | control.c \ 11 | pgut/pgut.c \ 12 | pgut/pgut-fe.c \ 13 | pgut/pgut-list.c 14 | 15 | OBJS = $(SRCS:.c=.o) 16 | PROGRAM = pg_statsinfo 17 | 18 | PG_CPPFLAGS = -I$(libpq_srcdir) 19 | PG_LIBS = $(libpq) 20 | 21 | ifndef USE_PGXS 22 | top_builddir = ../../.. 23 | makefile_global = $(top_builddir)/src/Makefile.global 24 | ifeq "$(wildcard $(makefile_global))" "" 25 | USE_PGXS = 1 # use pgxs if not in contrib directory 26 | endif 27 | endif 28 | 29 | ifdef USE_PGXS 30 | PG_CONFIG = pg_config 31 | PGXS := $(shell $(PG_CONFIG) --pgxs) 32 | include $(PGXS) 33 | else 34 | subdir = contrib/$(MODULE_big) 35 | include $(makefile_global) 36 | include $(top_srcdir)/contrib/contrib-global.mk 37 | endif 38 | 39 | # exclude libraries which are not required 40 | LIBS := $(filter -lpgport -lpgcommon, $(LIBS)) 41 | -------------------------------------------------------------------------------- /reporter/control.c: -------------------------------------------------------------------------------- 1 | /* 2 | * control.c 3 | * 4 | * Copyright (c) 2009-2025, NIPPON TELEGRAPH AND TELEPHONE CORPORATION 5 | */ 6 | 7 | #include "pg_statsinfo.h" 8 | 9 | /* 10 | * start pg_statsinfo background process 11 | */ 12 | void 13 | do_start(PGconn *conn) 14 | { 15 | PGresult *res; 16 | 17 | /* 18 | * call a function that start pg_statsinfo background process. 19 | * Note: 20 | * Not use pgut_command(), because don't want to write the error message 21 | * defined by pgut_command() to console. 22 | */ 23 | res = PQexec(conn, "SELECT statsinfo.start(60)"); 24 | 25 | if (PQresultStatus(res) != PGRES_COMMAND_OK) 26 | fprintf(stderr, "%s", PQerrorMessage(conn)); 27 | 28 | PQclear(res); 29 | } 30 | 31 | /* 32 | * stop pg_statsinfo background process 33 | */ 34 | void 35 | do_stop(PGconn *conn) 36 | { 37 | PGresult *res; 38 | 39 | /* 40 | * call a function that stop pg_statsinfo background process. 41 | * Note: 42 | * Not use pgut_command(), because don't want to write the error message 43 | * defined by pgut_command() to console. 44 | */ 45 | res = PQexec(conn, "SELECT statsinfo.stop(60)"); 46 | 47 | if (PQresultStatus(res) != PGRES_COMMAND_OK) 48 | fprintf(stderr, "%s", PQerrorMessage(conn)); 49 | 50 | PQclear(res); 51 | } 52 | -------------------------------------------------------------------------------- /reporter/pg_statsinfo.c: -------------------------------------------------------------------------------- 1 | /* 2 | * pg_statsinfo.c 3 | * 4 | * Copyright (c) 2009-2025, NIPPON TELEGRAPH AND TELEPHONE CORPORATION 5 | */ 6 | 7 | #include "pg_statsinfo.h" 8 | 9 | const char *PROGRAM_VERSION = "17.0"; 10 | const char *PROGRAM_URL = "https://github.com/ossc-db/pg_statsinfo/"; 11 | const char *PROGRAM_EMAIL = NULL; 12 | 13 | static bool mode_list; 14 | static bool mode_size; 15 | static bool mode_start; 16 | static bool mode_stop; 17 | static char *mode_report = NULL; 18 | static char *mode_snapshot = NULL; 19 | static char *mode_delete = NULL; 20 | static char *instid = NULL; 21 | static char *beginid = NULL; 22 | static char *endid = NULL; 23 | static time_t begindate = (time_t) -1; 24 | static time_t enddate = (time_t) -1; 25 | static char *output; 26 | 27 | /* options */ 28 | static struct pgut_option options[] = 29 | { 30 | { 'b', 'l', "list", &mode_list }, 31 | { 'b', 's', "size", &mode_size }, 32 | { 'b', 0x0, "start", &mode_start }, 33 | { 'b', 0x1, "stop", &mode_stop }, 34 | { 's', 'r', "report", &mode_report }, 35 | { 's', 'S', "snapshot", &mode_snapshot }, 36 | { 's', 'D', "delete", &mode_delete }, 37 | { 's', 'i', "instid", &instid }, 38 | { 's', 'b', "beginid", &beginid }, 39 | { 's', 'e', "endid", &endid }, 40 | { 't', 'B', "begindate", &begindate }, 41 | { 't', 'E', "enddate", &enddate }, 42 | { 's', 'o', "output", &output }, 43 | { 0 } 44 | }; 45 | 46 | int 47 | main(int argc, char *argv[]) 48 | { 49 | PGconn *conn; 50 | StringInfoData conn_info; 51 | int num_options; 52 | int mode_cnt; 53 | 54 | num_options = pgut_getopt(argc, argv, options); 55 | 56 | /* command-line arguments is not necessary */ 57 | if (num_options != argc) 58 | ereport(ERROR, 59 | (errcode(EINVAL), 60 | errmsg("too many argumetns"))); 61 | 62 | /* validity check of the mode option */ 63 | mode_cnt = 0; 64 | mode_cnt += mode_list ? 1 : 0; 65 | mode_cnt += mode_size ? 1 : 0; 66 | mode_cnt += mode_report ? 1 : 0; 67 | mode_cnt += mode_snapshot ? 1 : 0; 68 | mode_cnt += mode_delete ? 1 : 0; 69 | mode_cnt += mode_start ? 1 : 0; 70 | mode_cnt += mode_stop ? 1 : 0; 71 | if (mode_cnt == 0) 72 | ereport(ERROR, 73 | (errcode(EINVAL), 74 | errmsg("please specify operation option (-l, -s, -r, -S, -D)"))); 75 | else if (mode_cnt > 1) 76 | ereport(ERROR, 77 | (errcode(EINVAL), 78 | errmsg("can't specify two or more mode"))); 79 | 80 | /* connect to database */ 81 | initStringInfo(&conn_info); 82 | if (dbname && dbname[0]) 83 | appendStringInfo(&conn_info, "dbname=%s ", dbname); 84 | if (host && host[0]) 85 | appendStringInfo(&conn_info, "host=%s ", host); 86 | if (port && port[0]) 87 | appendStringInfo(&conn_info, "port=%s ", port); 88 | if (username && username[0]) 89 | appendStringInfo(&conn_info, "user=%s ", username); 90 | 91 | conn = pgut_connect(conn_info.data, prompt_password, ERROR); 92 | termStringInfo(&conn_info); 93 | 94 | /* execute a specified operation */ 95 | if (mode_list) 96 | do_list(conn, instid); 97 | else if (mode_size) 98 | do_size(conn); 99 | else if (mode_report) 100 | do_report(conn, mode_report, 101 | instid, beginid, endid, begindate, enddate, output); 102 | else if (mode_snapshot) 103 | do_snapshot(conn, mode_snapshot); 104 | else if (mode_delete) 105 | do_delete(conn, mode_delete); 106 | else if (mode_start) 107 | do_start(conn); 108 | else if (mode_stop) 109 | do_stop(conn); 110 | 111 | pgut_disconnect(conn); 112 | return 0; 113 | } 114 | 115 | void 116 | pgut_help(bool details) 117 | { 118 | printf("pg_statsinfo reports a PostgreSQL database.\n\n"); 119 | printf("Usage:\n"); 120 | printf(" pg_statsinfo -r REPORTID [-i INSTANCEID] [-b SNAPID] [-e SNAPID] [-B DATE] [-E DATE]\n"); 121 | printf(" [-o FILENAME] [connection-options]\n"); 122 | printf(" pg_statsinfo -l [-i INSTANCEID] [connection-options]\n"); 123 | printf(" pg_statsinfo -s [connection-options]\n"); 124 | printf(" pg_statsinfo -S COMMENT [connection-options]\n"); 125 | printf(" pg_statsinfo -D SNAPID [connection-options]\n"); 126 | printf(" pg_statsinfo --start [connection-options]\n"); 127 | printf(" pg_statsinfo --stop [connection-options]\n"); 128 | 129 | if (!details) 130 | return; 131 | 132 | printf("\nGeneral options:\n"); 133 | printf(" -r, --report=REPORTID generate a report that specified by REPORTID\n"); 134 | printf(" ---------------------------\n"); 135 | printf(" * Summary\n"); 136 | printf(" * Alert\n"); 137 | printf(" * DatabaseStatistics\n"); 138 | printf(" * InstanceActivity\n"); 139 | printf(" * OSResourceUsage\n"); 140 | printf(" * DiskUsage\n"); 141 | printf(" * LongTransactions\n"); 142 | printf(" * NotableTables\n"); 143 | printf(" * CheckpointActivity\n"); 144 | printf(" * AutovacuumActivity\n"); 145 | printf(" * QueryActivity\n"); 146 | printf(" * LockConflicts\n"); 147 | printf(" * ReplicationActivity\n"); 148 | printf(" * SettingParameters\n"); 149 | printf(" * SchemaInformation\n"); 150 | printf(" * Profiles\n"); 151 | printf(" * HardwareInformation\n"); 152 | printf(" * All\n"); 153 | printf(" ---------------------------\n"); 154 | printf(" (can prefix match. For example, \"su\" means 'Summary')\n"); 155 | printf(" -i, --instid limit to instances of specified instance ID\n"); 156 | printf(" -b, --beginid begin point of report scope (specify by snapshot ID)\n"); 157 | printf(" -B, --begindate begin point of report scope (specify by timestamp)\n"); 158 | printf(" -e, --endid end point of report scope (specify by snapshot ID)\n"); 159 | printf(" -E, --enddate end point of report scope (specify by timestamp)\n"); 160 | printf(" -l, --list show the snapshot list\n"); 161 | printf(" -s, --size show the snapshot size\n"); 162 | printf(" -S, --snapshot=COMMENT get a snapshot\n"); 163 | printf(" -D, --delete=SNAPID delete a snapshot\n"); 164 | printf(" --start start the pg_statsinfo agent\n"); 165 | printf(" --stop stop the pg_statsinfo agent\n"); 166 | printf("\nOutput options:\n"); 167 | printf(" -o, --output=FILENAME destination file path for report\n"); 168 | } 169 | -------------------------------------------------------------------------------- /reporter/pg_statsinfo.h: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | * 3 | * pg_statsinfo.h 4 | * 5 | * Copyright (c) 2009-2025, NIPPON TELEGRAPH AND TELEPHONE CORPORATION 6 | * 7 | *------------------------------------------------------------------------- 8 | */ 9 | 10 | #ifndef PG_STATSINFO_H 11 | #define PG_STATSINFO_H 12 | 13 | #include "postgres_fe.h" 14 | #include "pgut/pgut.h" 15 | #include "pgut/pgut-fe.h" 16 | #include "pgut/pgut-list.h" 17 | 18 | /* report ID */ 19 | #define REPORTID_SUMMARY "Summary" 20 | #define REPORTID_DATABASE_STATISTICS "DatabaseStatistics" 21 | #define REPORTID_INSTANCE_ACTIVITY "InstanceActivity" 22 | #define REPORTID_OS_RESOURCE_USAGE "OSResourceUsage" 23 | #define REPORTID_DISK_USAGE "DiskUsage" 24 | #define REPORTID_LONG_TRANSACTIONS "LongTransactions" 25 | #define REPORTID_NOTABLE_TABLES "NotableTables" 26 | #define REPORTID_CHECKPOINT_ACTIVITY "CheckpointActivity" 27 | #define REPORTID_AUTOVACUUM_ACTIVITY "AutovacuumActivity" 28 | #define REPORTID_QUERY_ACTIVITY "QueryActivity" 29 | #define REPORTID_LOCK_CONFLICTS "LockConflicts" 30 | #define REPORTID_REPLICATION_ACTIVITY "ReplicationActivity" 31 | #define REPORTID_SETTING_PARAMETERS "SettingParameters" 32 | #define REPORTID_SCHEMA_INFORMATION "SchemaInformation" 33 | #define REPORTID_ALERT "Alert" 34 | #define REPORTID_PROFILES "Profiles" 35 | #define REPORTID_HARDWARE_INFO "HardwareInformation" 36 | #define REPORTID_ALL "All" 37 | 38 | /* report.c */ 39 | extern void do_report(PGconn *conn, const char *reportid, const char *instid, 40 | const char *beginid, const char *endid, time_t begindate, 41 | time_t enddate, const char *filename); 42 | 43 | /* snapshot.c */ 44 | extern void do_list(PGconn *conn, const char *instid); 45 | extern void do_size(PGconn *conn); 46 | extern void do_snapshot(PGconn *conn, const char *comment); 47 | extern void do_delete(PGconn *conn, const char *targetid); 48 | 49 | /* control.c */ 50 | extern void do_start(PGconn *conn); 51 | extern void do_stop(PGconn *conn); 52 | 53 | #endif /* PG_STATSINFO_H */ 54 | -------------------------------------------------------------------------------- /reporter/pgut/pgut-fe.h: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | * 3 | * pgut-fe.h 4 | * 5 | * Copyright (c) 2009-2025, NIPPON TELEGRAPH AND TELEPHONE CORPORATION 6 | * 7 | *------------------------------------------------------------------------- 8 | */ 9 | 10 | #ifndef PGUT_FE_H 11 | #define PGUT_FE_H 12 | 13 | #include "pgut.h" 14 | 15 | typedef enum pgut_optsrc 16 | { 17 | SOURCE_DEFAULT, 18 | SOURCE_ENV, 19 | SOURCE_FILE, 20 | SOURCE_CMDLINE, 21 | SOURCE_CONST 22 | } pgut_optsrc; 23 | 24 | /* 25 | * type: 26 | * b: bool (true) 27 | * B: bool (false) 28 | * f: pgut_optfn 29 | * i: 32bit signed integer 30 | * u: 32bit unsigned integer 31 | * I: 64bit signed integer 32 | * U: 64bit unsigned integer 33 | * s: string 34 | * t: time_t 35 | * y: YesNo (YES) 36 | * Y: YesNo (NO) 37 | */ 38 | typedef struct pgut_option 39 | { 40 | char type; 41 | char sname; /* short name */ 42 | const char *lname; /* long name */ 43 | void *var; /* pointer to variable */ 44 | pgut_optsrc allowed; /* allowed source */ 45 | pgut_optsrc source; /* actual source */ 46 | } pgut_option; 47 | 48 | typedef void (*pgut_optfn) (pgut_option *opt, const char *arg); 49 | 50 | 51 | extern char *dbname; 52 | extern char *host; 53 | extern char *port; 54 | extern char *username; 55 | extern char *password; 56 | extern YesNo prompt_password; 57 | 58 | extern PGconn *connection; 59 | 60 | extern void pgut_help(bool details); 61 | extern void help(bool details); 62 | 63 | extern void disconnect(void); 64 | extern void reconnect(int elevel); 65 | extern PGresult *execute(const char *query, int nParams, const char **params); 66 | extern PGresult *execute_elevel(const char *query, int nParams, const char **params, int elevel); 67 | extern ExecStatusType command(const char *query, int nParams, const char **params); 68 | 69 | extern int pgut_getopt(int argc, char **argv, pgut_option options[]); 70 | extern void pgut_readopt(const char *path, pgut_option options[], int elevel); 71 | extern void pgut_setopt(pgut_option *opt, const char *optarg, pgut_optsrc src); 72 | extern bool pgut_keyeq(const char *lhs, const char *rhs); 73 | 74 | #endif /* PGUT_FE_H */ 75 | -------------------------------------------------------------------------------- /reporter/pgut/pgut-list.h: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | * 3 | * pgut-list.h : copied from postgres/nodes/pg_list.h 4 | * 5 | * Copyright (c) 2009-2025, NIPPON TELEGRAPH AND TELEPHONE CORPORATION 6 | * 7 | *------------------------------------------------------------------------- 8 | */ 9 | 10 | #ifndef PGUT_LIST_H 11 | #define PGUT_LIST_H 12 | 13 | #include "nodes/nodes.h" 14 | 15 | typedef struct ListCell ListCell; 16 | 17 | typedef struct List 18 | { 19 | NodeTag type; /* T_List, T_IntList, or T_OidList */ 20 | int length; 21 | ListCell *head; 22 | ListCell *tail; 23 | } List; 24 | 25 | struct ListCell 26 | { 27 | union 28 | { 29 | void *ptr_value; 30 | int int_value; 31 | Oid oid_value; 32 | } data; 33 | ListCell *next; 34 | }; 35 | 36 | /* 37 | * The *only* valid representation of an empty list is NIL; in other 38 | * words, a non-NIL list is guaranteed to have length >= 1 and 39 | * head/tail != NULL 40 | */ 41 | #define NIL ((List *) NULL) 42 | 43 | /* 44 | * These routines are used frequently. However, we can't implement 45 | * them as macros, since we want to avoid double-evaluation of macro 46 | * arguments. Therefore, we implement them using static inline functions 47 | * if supported by the compiler, or as regular functions otherwise. 48 | */ 49 | #ifndef __GNUC__ 50 | extern ListCell *list_head(const List *l); 51 | extern ListCell *list_tail(List *l); 52 | extern int list_length(const List *l); 53 | #else 54 | static inline ListCell * 55 | list_head(const List *l) 56 | { 57 | return l ? l->head : NULL; 58 | } 59 | 60 | static inline ListCell * 61 | list_tail(List *l) 62 | { 63 | return l ? l->tail : NULL; 64 | } 65 | 66 | static inline int 67 | list_length(const List *l) 68 | { 69 | return l ? l->length : 0; 70 | } 71 | #endif /* ! __GNUC__ */ 72 | 73 | /* 74 | * NB: There is an unfortunate legacy from a previous incarnation of 75 | * the List API: the macro lfirst() was used to mean "the data in this 76 | * cons cell". To avoid changing every usage of lfirst(), that meaning 77 | * has been kept. As a result, lfirst() takes a ListCell and returns 78 | * the data it contains; to get the data in the first cell of a 79 | * List, use linitial(). Worse, lsecond() is more closely related to 80 | * linitial() than lfirst(): given a List, lsecond() returns the data 81 | * in the second cons cell. 82 | */ 83 | #define lnext(lc) ((lc)->next) 84 | #define lfirst(lc) ((lc)->data.ptr_value) 85 | #define lfirst_int(lc) ((lc)->data.int_value) 86 | #define lfirst_oid(lc) ((lc)->data.oid_value) 87 | 88 | #define linitial(l) lfirst(list_head(l)) 89 | #define linitial_int(l) lfirst_int(list_head(l)) 90 | #define linitial_oid(l) lfirst_oid(list_head(l)) 91 | 92 | #define lsecond(l) lfirst(lnext(list_head(l))) 93 | #define lsecond_int(l) lfirst_int(lnext(list_head(l))) 94 | #define lsecond_oid(l) lfirst_oid(lnext(list_head(l))) 95 | 96 | #define lthird(l) lfirst(lnext(lnext(list_head(l)))) 97 | #define lthird_int(l) lfirst_int(lnext(lnext(list_head(l)))) 98 | #define lthird_oid(l) lfirst_oid(lnext(lnext(list_head(l)))) 99 | 100 | #define lfourth(l) lfirst(lnext(lnext(lnext(list_head(l))))) 101 | #define lfourth_int(l) lfirst_int(lnext(lnext(lnext(list_head(l))))) 102 | #define lfourth_oid(l) lfirst_oid(lnext(lnext(lnext(list_head(l))))) 103 | 104 | #define llast(l) lfirst(list_tail(l)) 105 | #define llast_int(l) lfirst_int(list_tail(l)) 106 | #define llast_oid(l) lfirst_oid(list_tail(l)) 107 | 108 | /* 109 | * foreach - 110 | * a convenience macro which loops through the list 111 | */ 112 | #define foreach(cell, l) \ 113 | for ((cell) = list_head(l); (cell) != NULL; (cell) = lnext(cell)) 114 | 115 | /* 116 | * for_each_cell - 117 | * a convenience macro which loops through a list starting from a 118 | * specified cell 119 | */ 120 | #define for_each_cell(cell, initcell) \ 121 | for ((cell) = (initcell); (cell) != NULL; (cell) = lnext(cell)) 122 | 123 | /* 124 | * forboth - 125 | * a convenience macro for advancing through two linked lists 126 | * simultaneously. This macro loops through both lists at the same 127 | * time, stopping when either list runs out of elements. Depending 128 | * on the requirements of the call site, it may also be wise to 129 | * assert that the lengths of the two lists are equal. 130 | */ 131 | #define forboth(cell1, list1, cell2, list2) \ 132 | for ((cell1) = list_head(list1), (cell2) = list_head(list2); \ 133 | (cell1) != NULL && (cell2) != NULL; \ 134 | (cell1) = lnext(cell1), (cell2) = lnext(cell2)) 135 | 136 | /* 137 | * forthree - 138 | * the same for three lists 139 | */ 140 | #define forthree(cell1, list1, cell2, list2, cell3, list3) \ 141 | for ((cell1) = list_head(list1), (cell2) = list_head(list2), (cell3) = list_head(list3); \ 142 | (cell1) != NULL && (cell2) != NULL && (cell3) != NULL; \ 143 | (cell1) = lnext(cell1), (cell2) = lnext(cell2), (cell3) = lnext(cell3)) 144 | 145 | extern List *lappend(List *list, void *datum); 146 | extern ListCell *lappend_cell(List *list, ListCell *prev, void *datum); 147 | extern List *lcons(void *datum, List *list); 148 | extern List *list_concat(List *list1, List *list2); 149 | extern List *list_truncate(List *list, int new_size); 150 | extern void *list_nth(const List *list, int n); 151 | extern bool list_member_ptr(const List *list, const void *datum); 152 | extern List *list_delete_ptr(List *list, void *datum); 153 | extern List *list_delete_first(List *list); 154 | extern List *list_delete_cell(List *list, ListCell *cell, ListCell *prev); 155 | extern void list_free(List *list); 156 | extern void list_free_deep(List *list); 157 | extern List *list_copy(const List *list); 158 | extern List *list_copy_tail(const List *list, int nskip); 159 | 160 | /* 161 | * pgut-list.c : Extended list functions 162 | */ 163 | extern void list_walk(List *list, void (*walker)()); 164 | extern void list_destroy(List *list, void (*walker)()); 165 | 166 | #endif /* PGUT_LIST_H */ 167 | -------------------------------------------------------------------------------- /reporter/pgut/pgut.h: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | * 3 | * pgut.h 4 | * 5 | * Copyright (c) 2009-2025, NIPPON TELEGRAPH AND TELEPHONE CORPORATION 6 | * 7 | *------------------------------------------------------------------------- 8 | */ 9 | 10 | #ifndef PGUT_H 11 | #define PGUT_H 12 | 13 | #include "c.h" 14 | #include 15 | 16 | #ifndef WIN32 17 | #include 18 | #include 19 | #endif 20 | 21 | #include "libpq-fe.h" 22 | #include "pqexpbuffer.h" 23 | #include "utils/elog.h" 24 | 25 | #define INFINITE_STR "INFINITE" 26 | 27 | typedef enum YesNo 28 | { 29 | DEFAULT, 30 | NO, 31 | YES 32 | } YesNo; 33 | 34 | typedef void (*pgut_atexit_callback)(bool fatal, void *userdata); 35 | 36 | /* 37 | * pgut client variables and functions 38 | */ 39 | extern const char *PROGRAM_NAME; 40 | extern const char *PROGRAM_VERSION; 41 | extern const char *PROGRAM_URL; 42 | extern const char *PROGRAM_EMAIL; 43 | 44 | /* 45 | * pgut framework variables and functions 46 | */ 47 | extern bool interrupted; 48 | extern int pgut_log_level; 49 | extern int pgut_abort_level; 50 | extern bool pgut_echo; 51 | 52 | extern void pgut_init(int argc, char **argv); 53 | extern void pgut_atexit_push(pgut_atexit_callback callback, void *userdata); 54 | extern void pgut_atexit_pop(pgut_atexit_callback callback, void *userdata); 55 | extern void pgut_putenv(const char *key, const char *value); 56 | 57 | /* 58 | * Database connections 59 | */ 60 | extern PGconn *pgut_connect(const char *info, YesNo prompt, int elevel); 61 | extern void pgut_disconnect(PGconn *conn); 62 | extern void pgut_disconnect_all(void); 63 | extern PGresult *pgut_execute(PGconn* conn, const char *query, int nParams, const char **params); 64 | PGresult *pgut_execute_elevel(PGconn* conn, const char *query, int nParams, const char **params, int elevel); 65 | extern ExecStatusType pgut_command(PGconn* conn, const char *query, int nParams, const char **params); 66 | extern bool pgut_commit(PGconn *conn); 67 | extern void pgut_rollback(PGconn *conn); 68 | extern bool pgut_send(PGconn* conn, const char *query, int nParams, const char **params); 69 | extern int pgut_wait(int num, PGconn *connections[], struct timeval *timeout); 70 | 71 | /* 72 | * memory allocators 73 | */ 74 | extern void *pgut_malloc(size_t size); 75 | extern void *pgut_realloc(void *p, size_t size); 76 | extern char *pgut_strdup(const char *str); 77 | extern char *strdup_with_len(const char *str, size_t len); 78 | extern char *strdup_trim(const char *str); 79 | 80 | #define pgut_new(type) ((type *) pgut_malloc(sizeof(type))) 81 | #define pgut_newarray(type, n) ((type *) pgut_malloc(sizeof(type) * (n))) 82 | #define pgut_newvar(type, m, n) ((type *) pgut_malloc(offsetof(type, m) + (n))) 83 | 84 | /* 85 | * file operations 86 | */ 87 | extern FILE *pgut_fopen(const char *path, const char *mode); 88 | extern bool pgut_mkdir(const char *path); 89 | 90 | /* 91 | * elog 92 | */ 93 | #define E_PG_CONNECT (-1) /* PostgreSQL connection error */ 94 | #define E_PG_COMMAND (-2) /* PostgreSQL query or command error */ 95 | 96 | #undef elog 97 | #undef ereport 98 | #define ereport(elevel, rest) \ 99 | (pgut_errstart(elevel) ? (pgut_errfinish rest) : (void) 0) 100 | 101 | extern void elog(int elevel, const char *fmt, ...) 102 | __attribute__((format(printf, 2, 3))); 103 | extern const char *format_elevel(int elevel); 104 | extern int parse_elevel(const char *value); 105 | extern int errcode_errno(void); 106 | extern bool log_required(int elevel, int log_min_level); 107 | extern bool pgut_errstart(int elevel); 108 | extern void pgut_errfinish(int dummy, ...); 109 | extern void pgut_error(int elevel, int code, const char *msg, const char *detail); 110 | 111 | /* 112 | * CHECK_FOR_INTERRUPTS 113 | */ 114 | #undef CHECK_FOR_INTERRUPTS 115 | extern void CHECK_FOR_INTERRUPTS(void); 116 | 117 | /* 118 | * Assert 119 | */ 120 | #undef Assert 121 | #undef AssertArg 122 | #undef AssertMacro 123 | 124 | #ifdef USE_ASSERT_CHECKING 125 | #define Assert(x) assert(x) 126 | #define AssertArg(x) assert(x) 127 | #define AssertMacro(x) assert(x) 128 | #else 129 | #define Assert(x) ((void) 0) 130 | #define AssertArg(x) ((void) 0) 131 | #define AssertMacro(x) ((void) 0) 132 | #endif 133 | 134 | /* 135 | * StringInfo and string operations 136 | */ 137 | #define STRINGINFO_H 138 | 139 | #define StringInfoData PQExpBufferData 140 | #define StringInfo PQExpBuffer 141 | #define makeStringInfo createPQExpBuffer 142 | #define initStringInfo initPQExpBuffer 143 | #define freeStringInfo destroyPQExpBuffer 144 | #define termStringInfo termPQExpBuffer 145 | #define resetStringInfo resetPQExpBuffer 146 | #define enlargeStringInfo enlargePQExpBuffer 147 | #define printfStringInfo printfPQExpBuffer /* reset + append */ 148 | #define appendStringInfo appendPQExpBuffer 149 | #define appendStringInfoString appendPQExpBufferStr 150 | #define appendStringInfoChar appendPQExpBufferChar 151 | #define appendBinaryStringInfo appendBinaryPQExpBuffer 152 | 153 | extern bool appendStringInfoVA2(StringInfo str, const char *fmt, va_list args) 154 | __attribute__((format(PG_PRINTF_ATTRIBUTE, 2, 0))); 155 | extern int appendStringInfoFile(StringInfo str, FILE *fp); 156 | extern int appendStringInfoFd(StringInfo str, int fd); 157 | 158 | extern bool parse_bool(const char *value, bool *result); 159 | extern bool parse_bool_with_len(const char *value, size_t len, bool *result); 160 | extern bool parse_int32(const char *value, int32 *result); 161 | extern bool parse_uint32(const char *value, uint32 *result); 162 | extern bool parse_int64(const char *value, int64 *result); 163 | extern bool parse_uint64(const char *value, uint64 *result); 164 | extern bool parse_time(const char *value, time_t *time); 165 | 166 | #define IsSpace(c) (isspace((unsigned char)(c))) 167 | #define IsAlpha(c) (isalpha((unsigned char)(c))) 168 | #define IsAlnum(c) (isalnum((unsigned char)(c))) 169 | #define IsIdentHead(c) (IsAlpha(c) || (c) == '_') 170 | #define IsIdentBody(c) (IsAlnum(c) || (c) == '_') 171 | #define ToLower(c) (tolower((unsigned char)(c))) 172 | #define ToUpper(c) (toupper((unsigned char)(c))) 173 | 174 | /* 175 | * socket operations 176 | */ 177 | extern int wait_for_socket(int sock, struct timeval *timeout); 178 | extern int wait_for_sockets(int nfds, fd_set *fds, struct timeval *timeout); 179 | 180 | #ifdef WIN32 181 | extern int sleep(unsigned int seconds); 182 | extern int usleep(unsigned int usec); 183 | #endif 184 | 185 | #endif /* PGUT_H */ 186 | -------------------------------------------------------------------------------- /reporter/snapshot.c: -------------------------------------------------------------------------------- 1 | /* 2 | * snapshot.c 3 | * 4 | * Copyright (c) 2009-2025, NIPPON TELEGRAPH AND TELEPHONE CORPORATION 5 | */ 6 | 7 | #include "pg_statsinfo.h" 8 | 9 | #define SQL_SELECT_SNAPSHOT_LIST "\ 10 | SELECT \ 11 | s.snapid, \ 12 | i.instid, \ 13 | i.hostname, \ 14 | i.port, \ 15 | s.time::timestamp(0), \ 16 | s.comment, \ 17 | s.exec_time::interval(0), \ 18 | statsrepo.pg_size_pretty(s.snapshot_increase_size) \ 19 | FROM \ 20 | statsrepo.snapshot s \ 21 | LEFT JOIN statsrepo.instance i ON s.instid = i.instid" 22 | 23 | #define SQL_SELECT_SNAPSHOT_SIZE "\ 24 | SELECT \ 25 | i.instid, \ 26 | i.name, \ 27 | i.hostname, \ 28 | i.port, \ 29 | pg_catalog.count(*), \ 30 | pg_catalog.sum(s.snapshot_increase_size), \ 31 | pg_catalog.max(s.snapid), \ 32 | pg_catalog.max(s.time)::timestamp(0) \ 33 | FROM \ 34 | statsrepo.snapshot s \ 35 | LEFT JOIN statsrepo.instance i ON s.instid = i.instid \ 36 | GROUP BY \ 37 | i.instid, \ 38 | i.name, \ 39 | i.hostname, \ 40 | i.port \ 41 | ORDER BY \ 42 | i.instid" 43 | 44 | static char *size_pretty(int64 size); 45 | 46 | /* 47 | * show the snapshot list 48 | */ 49 | void 50 | do_list(PGconn *conn, const char *instid) 51 | { 52 | PGresult *res; 53 | StringInfoData query; 54 | int64 i_id; 55 | int i; 56 | 57 | initStringInfo(&query); 58 | appendStringInfo(&query, SQL_SELECT_SNAPSHOT_LIST); 59 | 60 | /* get the snapshot list */ 61 | if (instid) 62 | { 63 | /* validate value of instance ID */ 64 | if (!parse_int64(instid, &i_id) || i_id <= 0) 65 | ereport(ERROR, 66 | (errcode(EINVAL), 67 | errmsg("invalid instance ID (--instid) : '%s'", instid))); 68 | 69 | /* get a list of the specified instances snapshot */ 70 | appendStringInfo(&query, "\nWHERE i.instid = $1"); 71 | appendStringInfo(&query, "\nORDER BY s.snapid"); 72 | res = pgut_execute(conn, query.data, 1, &instid); 73 | } 74 | else 75 | { 76 | /* get a list of all instances snapshot */ 77 | appendStringInfo(&query, "\nORDER BY s.snapid"); 78 | res = pgut_execute(conn, query.data, 0, NULL); 79 | } 80 | 81 | /* show the snapshot list */ 82 | printf("----------------------------------------\n"); 83 | printf("Snapshot List\n"); 84 | printf("----------------------------------------\n"); 85 | printf("%10s %10s %-32s %8s %20s %-20s %12s %8s\n", 86 | "SnapshotID", "InstanceID", "Host", "Port", "Timestamp", "Comment", "Execute Time", "Size"); 87 | printf("-----------------------------------------------------------------------------------------------------------------------------------------\n"); 88 | for(i = 0; i < PQntuples(res); i++) 89 | { 90 | printf("%10s %10s %-32s %8s %20s %-20s %12s %8s\n", 91 | PQgetvalue(res, i, 0), 92 | PQgetvalue(res, i, 1), 93 | PQgetvalue(res, i, 2), 94 | PQgetvalue(res, i, 3), 95 | PQgetvalue(res, i, 4), 96 | PQgetvalue(res, i, 5), 97 | PQgetvalue(res, i, 6), 98 | PQgetvalue(res, i, 7)); 99 | } 100 | PQclear(res); 101 | termStringInfo(&query); 102 | } 103 | 104 | /* 105 | * show the information about size of snapshot 106 | */ 107 | void 108 | do_size(PGconn *conn) 109 | { 110 | PGresult *res; 111 | StringInfoData query; 112 | int64 total_size = 0; 113 | char *pretty_size; 114 | int i; 115 | 116 | initStringInfo(&query); 117 | appendStringInfo(&query, SQL_SELECT_SNAPSHOT_SIZE); 118 | 119 | /* get snapshot size of each instance */ 120 | res = pgut_execute(conn, query.data, 0, NULL); 121 | 122 | /* show the snapshot size for each instance */ 123 | printf("----------------------------------------\n"); 124 | printf("Snapshot Size Information\n"); 125 | printf("----------------------------------------\n"); 126 | for (i = 0; i < PQntuples(res); i++) 127 | { 128 | int64 size; 129 | 130 | /* convert the value of snapshot size */ 131 | parse_int64(PQgetvalue(res, i, 5), &size); /* no need to validate values */ 132 | pretty_size = size_pretty(size); 133 | 134 | printf("Instance ID : %s\n", PQgetvalue(res, i, 0)); 135 | printf("Database System ID : %s\n", PQgetvalue(res, i, 1)); 136 | printf("Host : %s\n", PQgetvalue(res, i, 2)); 137 | printf("Port : %s\n", PQgetvalue(res, i, 3)); 138 | printf("Number Of Snapshots : %s\n", PQgetvalue(res, i, 4)); 139 | printf("Snapshot Size : %s\n", pretty_size); 140 | printf("Latest Snapshot ID : %s\n", PQgetvalue(res, i, 6)); 141 | printf("Latest Snapshot Timestamp : %s\n\n", PQgetvalue(res, i, 7)); 142 | 143 | total_size += size; 144 | free(pretty_size); 145 | } 146 | /* total size of the snapshot of all instances */ 147 | pretty_size = size_pretty(total_size); 148 | printf("Total Snapshot Size : %s\n\n", pretty_size); 149 | 150 | PQclear(res); 151 | termStringInfo(&query); 152 | free(pretty_size); 153 | } 154 | 155 | /* 156 | * get a snapshot 157 | */ 158 | void 159 | do_snapshot(PGconn *conn, const char *comment) 160 | { 161 | /* call a function that get snapshot */ 162 | pgut_command(conn, "SELECT statsinfo.snapshot($1)", 1, &comment); 163 | } 164 | 165 | /* 166 | * delete a snapshot 167 | */ 168 | void 169 | do_delete(PGconn *conn, const char *targetid) 170 | { 171 | int64 snapid; 172 | 173 | /* validate value of snapshot ID */ 174 | if (!parse_int64(targetid, &snapid) || snapid <= 0) 175 | ereport(ERROR, 176 | (errcode(EINVAL), 177 | errmsg("invalid snapshot ID: '%s'", targetid))); 178 | 179 | /* call a function that delete snapshot */ 180 | pgut_command(conn, "SELECT statsrepo.del_snapshot($1::bigint)", 1, &targetid); 181 | } 182 | 183 | /* 184 | * formatting with size units 185 | */ 186 | static char * 187 | size_pretty(int64 size) 188 | { 189 | char buf[64]; 190 | int64 limit = 10 * 1024; 191 | int64 mult = 1; 192 | 193 | if (size < limit * mult) 194 | snprintf(buf, sizeof(buf), INT64_FORMAT " bytes", size); 195 | else 196 | { 197 | mult *= 1024; 198 | if (size < limit * mult) 199 | snprintf(buf, sizeof(buf), INT64_FORMAT " KiB", 200 | (size + mult / 2) / mult); 201 | else 202 | { 203 | mult *= 1024; 204 | if (size < limit * mult) 205 | snprintf(buf, sizeof(buf), INT64_FORMAT " MiB", 206 | (size + mult / 2) / mult); 207 | else 208 | { 209 | mult *= 1024; 210 | if (size < limit * mult) 211 | snprintf(buf, sizeof(buf), INT64_FORMAT " GiB", 212 | (size + mult / 2) / mult); 213 | else 214 | { 215 | /* Here we have to worry about avoiding overflow */ 216 | int64 val; 217 | 218 | mult *= 1024; 219 | val = size / mult; 220 | if ((size % mult) >= (mult / 2)) 221 | val++; 222 | snprintf(buf, sizeof(buf), INT64_FORMAT " TiB", 223 | val); 224 | } 225 | } 226 | } 227 | } 228 | 229 | return pgut_strdup(buf); 230 | } 231 | -------------------------------------------------------------------------------- /spec/README: -------------------------------------------------------------------------------- 1 | How to create a pg_statsinfo RPM package. 2 | ===================================== 3 | 4 | 1. Create the work directories required by rpmbuild. 5 | 6 | $ mkdir -p ~/rpmbuild/{BUILD,RPMS,SOURCES,SPECS,SRPMS} 7 | 8 | 2. Create the .rpmmacros file so that rpmbuild will use the 9 | directory above. 10 | 11 | $ echo "%_topdir %(echo $HOME)/rpmbuild" > ~/.rpmmacros 12 | 13 | 3. Place files in the rpmbuild directory. 14 | 15 | $ cp pg_statsinfo-X.X.X.tar.gz ~/rpmbuild/SOURCES 16 | $ cp pg_statsinfo.spec ~/rpmbuild/SPECS 17 | 18 | 4. Edit the SPEC file as needed. The following two lines will be 19 | required to be changed. 20 | 21 | $ vi ~/rpmbuild/SPECS/pg_statsinfo.spec 22 | ---------------------------------------- 23 | %define _pgdir /usr/pgsql-9.4 24 | BuildRequires: postgresql94-devel 25 | ---------------------------------------- 26 | 27 | 5. Run rpmbuild. "-bb" instructs to build a binary package. 28 | 29 | $ rpmbuild -bb ~/rpmbuild/SPECS/pg_statsinfo.spec 30 | 31 | 32 | Prerequisite packages. 33 | ------------------------------------- 34 | The following packages should be installed beforehand. 35 | 36 | * rpm-build 37 | * postgresql-devel 38 | -------------------------------------------------------------------------------- /spec/pg_statsinfo-tmpfiles.d.conf: -------------------------------------------------------------------------------- 1 | d /run/pg_statsinfo 0755 postgres postgres - 2 | -------------------------------------------------------------------------------- /spec/pg_statsinfo.spec: -------------------------------------------------------------------------------- 1 | # SPEC file for pg_statsinfo 2 | # Copyright (c) 2009-2025, NIPPON TELEGRAPH AND TELEPHONE CORPORATION 3 | 4 | # Original declaration for pg_statsinfo rpmbuild # 5 | 6 | %define _pgdir /usr/pgsql-17 7 | %define _bindir %{_pgdir}/bin 8 | %define _libdir %{_pgdir}/lib 9 | %define _datadir %{_pgdir}/share 10 | 11 | %global packageversion 17 12 | %global tmpfilesconf spec/pg_statsinfo-tmpfiles.d.conf 13 | 14 | ## Set general information for pg_statsinfo. 15 | Name: pg_statsinfo 16 | Version: %{packageversion}.0 17 | Release: 1%{?dist} 18 | Summary: Performance monitoring tool for PostgreSQL 19 | Group: Applications/Databases 20 | License: BSD 21 | URL: https://github.com/ossc-db/pg_statsinfo 22 | Source0: %{name}-%{version}.tar.gz 23 | BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-%(%{__id_u} -n) 24 | 25 | ## We use postgresql-devel package 26 | BuildRequires: postgresql17-devel 27 | %if %{rhel} == 7 28 | BuildRequires: llvm-toolset-7 llvm5.0 29 | %endif 30 | %if %{rhel} == 8 31 | BuildRequires: llvm >= 6 32 | %endif 33 | %if %{rhel} == 9 34 | BuildRequires: llvm >= 15 35 | %endif 36 | 37 | %description 38 | pg_statsinfo monitors an instance of PostgreSQL server and gather 39 | the statistics and activities of the server as snapshots. 40 | 41 | %package llvmjit 42 | Requires: postgresql17-llvmjit 43 | Requires: pg_statsinfo = %{version} 44 | Summary: Just-in-time compilation support for pg_statsinfo 45 | 46 | %description llvmjit 47 | Just-in-time compilation support for pg_statsinfo 48 | 49 | ## pre work for build pg_statsinfo 50 | %prep 51 | %setup -q -n %{name}-%{version} 52 | 53 | ## Set variables for build environment 54 | %build 55 | USE_PGXS=1 make %{?_smp_mflags} 56 | 57 | ## Set variables for install 58 | %install 59 | rm -rf %{buildroot} 60 | USE_PGXS=1 make DESTDIR=%{buildroot} install 61 | %{__install} -d -m 755 %{buildroot}/%{_rundir}/%{name} 62 | %{__mkdir} -p %{buildroot}/%{_tmpfilesdir} 63 | %{__install} -m 0644 %{tmpfilesconf} %{buildroot}/%{_tmpfilesdir}/%{name}-%{packageversion}.conf 64 | 65 | %clean 66 | rm -rf %{buildroot} 67 | 68 | ## Set files for this packages 69 | %files 70 | %defattr(-,root,root) 71 | %{_bindir}/pg_statsinfo 72 | %{_bindir}/pg_statsinfod 73 | %{_bindir}/archive_pglog.sh 74 | %{_libdir}/pg_statsinfo.so 75 | %{_datadir}/contrib/pg_statsrepo.sql 76 | %{_datadir}/contrib/pg_statsrepo_alert.sql 77 | %{_datadir}/contrib/uninstall_pg_statsrepo.sql 78 | %{_datadir}/contrib/pg_statsinfo.sql 79 | %{_datadir}/contrib/uninstall_pg_statsinfo.sql 80 | %{_tmpfilesdir}/%{name}-%{packageversion}.conf 81 | %attr(755,postgres,postgres) %dir %{_rundir}/%{name} 82 | 83 | %files llvmjit 84 | %defattr(-,root,root) 85 | %{_libdir}/bitcode/pg_statsinfo.index.bc 86 | %{_libdir}/bitcode/pg_statsinfo/libstatsinfo.bc 87 | %{_libdir}/bitcode/pg_statsinfo/last_xact_activity.bc 88 | %{_libdir}/bitcode/pg_statsinfo/pg_control.bc 89 | %{_libdir}/bitcode/pg_statsinfo/port.bc 90 | %{_libdir}/bitcode/pg_statsinfo/wait_sampling.bc 91 | %{_libdir}/bitcode/pg_statsinfo/pgut/pgut-spi.bc 92 | 93 | ## Script to run just before installing the package 94 | %pre 95 | # Check if we can safely upgrade. 96 | # An upgrade is only safe if it's from one of our RPMs in the same version family. 97 | installed=$(rpm -q --whatprovides pg_statsinfo 2> /dev/null) 98 | if [ ${?} -eq 0 -a -n "${installed}" ] ; then 99 | old_version=$(rpm -q --queryformat='%{VERSION}' "${installed}" 2>&1) 100 | new_version='%{version}' 101 | 102 | new_family=$(echo ${new_version} | cut -d '.' -f 1) 103 | old_family=$(echo ${old_version} | cut -d '.' -f 1) 104 | 105 | [ -z "${old_family}" ] && old_family="" 106 | [ -z "${new_family}" ] && new_family="" 107 | 108 | if [ "${old_family}" != "${new_family}" ] ; then 109 | cat << EOF >&2 110 | ****************************************************************** 111 | A pg_statsinfo package ($installed) is already installed. 112 | Could not upgrade pg_statsinfo "${old_version}" to "${new_version}". 113 | A manual upgrade is required. 114 | 115 | - Remove 'pg_statsinfo' from shared_preload_libraries and 116 | all of pg_statsinfo.* parameters in postgresql.conf. 117 | - Restart the monitored database. 118 | - Uninstall statsinfo schema from monitored database. 119 | - Uninstall statsrepo schema from repository database. 120 | Snapshot is removed by dropping the statsrepo schema. 121 | Therefore, please backup the repository database as necessary. 122 | - Remove the existing pg_statsinfo package. 123 | - Install the new pg_statsinfo package. 124 | - Restore the parameters of postgresql.conf which was removed first. 125 | - Restart the monitored database. 126 | 127 | This is a brief description of the upgrade process. 128 | Important details can be found in the pg_statsinfo manual. 129 | ****************************************************************** 130 | EOF 131 | exit 1 132 | fi 133 | fi 134 | 135 | # History of pg_statsinfo-v15 RPM. 136 | %changelog 137 | * Wed Feb 19 2025 - NTT OSS Center 17.0-1 138 | - pg_stats_reporter 17.0 released 139 | * Thu Feb 29 2024 - NTT OSS Center 16.0-1 140 | - pg_stats_reporter 16.0 released 141 | * Wed Oct 18 2023 - NTT OSS Center 15.2-1 142 | - pg_statsinfo 15.2 released 143 | * Fri Jun 30 2023 - NTT OSS Center 15.1-1 144 | - pg_statsinfo 15.1 released 145 | * Wed Feb 1 2023 - NTT OSS Center 15.0-1 146 | - pg_statsinfo 15.0 released 147 | * Tue Feb 1 2022 - NTT OSS Center 14.0-1 148 | - pg_statsinfo 14.0 released 149 | * Mon Dec 14 2020 - NTT OSS Center 13.0-1 150 | - pg_statsinfo 13.0 released 151 | * Fri Feb 28 2020 - NTT OSS Center 12.1-1 152 | - pg_statsinfo 12.1 released 153 | * Fri Jan 24 2020 - NTT OSS Center 12.0-1 154 | - pg_statsinfo 12.0 released 155 | -------------------------------------------------------------------------------- /test/expected/function-alert.out: -------------------------------------------------------------------------------- 1 | /*---- Initialize repository DB ----*/ 2 | /*---- Initialize monitored instance ----*/ 3 | /*---- Alert Function ----*/ 4 | /**--- Alert the number of rollbacks per second ---**/ 5 | UPDATE 1 6 | BEGIN 7 | CREATE TABLE 8 | ROLLBACK 9 | ANALYZE 10 | ALERT: pg_statsinfo: too many rollbacks in snapshots between 'xxx' and 'xxx' --- xxx Rollbacks/sec (threshold = 0 Rollbacks/sec) 11 | /**--- Alert the number of commits per second ---**/ 12 | UPDATE 1 13 | BEGIN 14 | CREATE TABLE 15 | COMMIT 16 | ANALYZE 17 | ALERT: pg_statsinfo: too many transactions in snapshots between 'xxx' and 'xxx' --- xxx Transactions/sec (threshold = 0 Transactions/sec) 18 | /**--- Alert the response time average of query ---**/ 19 | UPDATE 1 20 | ALERT: pg_statsinfo: Query average response time exceeds threshold in snapshots between 'xxx' and 'xxx' --- xxx sec (threshold = 0 sec) 21 | /**--- Alert the response time max of query ---**/ 22 | UPDATE 1 23 | ALERT: pg_statsinfo: Query worst response time exceeds threshold in snapshots between 'xxx' and 'xxx' --- xxx sec (threshold = 0 sec) 24 | /**--- Alert the dead tuple size and ratio ---**/ 25 | UPDATE 1 26 | UPDATE 1 27 | UPDATE 1 28 | CREATE TABLE 29 | CREATE TABLE 30 | INSERT 0 500000 31 | INSERT 0 500000 32 | DELETE 400000 33 | DELETE 300000 34 | ANALYZE 35 | ALERT: pg_statsinfo: dead tuple size exceeds threshold in snapshot 'xxx' --- xxx MiB (threshold = 0 MiB) 36 | ALERT: pg_statsinfo: dead tuple ratio exceeds threshold in snapshot 'xxx' --- xxx % (threshold = 30 %) 37 | ALERT: pg_statsinfo: dead tuple ratio in 'postgres.public.tbl02' exceeds threshold in snapshot 'xxx' --- xxx % (threshold = 60 %) 38 | /**--- Alert the correlation of table ---**/ 39 | UPDATE 1 40 | SET 41 | CREATE TABLE 42 | ALTER TABLE 43 | INSERT 0 1 44 | INSERT 0 1 45 | INSERT 0 1 46 | INSERT 0 1 47 | INSERT 0 1 48 | ANALYZE 49 | ALERT: pg_statsinfo: correlation of the clustered table fell below threshold in snapshot 'xxx' --- 'postgres.public.tbl04', 70 % (threshold = 100 %) 50 | /**--- Alert the number of backend processes ---**/ 51 | UPDATE 1 52 | ALERT: pg_statsinfo: too many backends in snapshot 'xxx' --- 2 (threshold = 0) 53 | /**--- Alert the condition of the OS resource ---**/ 54 | UPDATE 1 55 | UPDATE 1 56 | UPDATE 1 57 | UPDATE 1 58 | UPDATE 1 59 | UPDATE 1 60 | alert 61 | -------------------------------------------------------------------------------------------------------------------------- 62 | free disk space ratio at 'pg_default' fell below threshold in snapshot 'xxx' --- 19 % (threshold = 20 %) 63 | load average 1min exceeds threshold in snapshot 'xxx' --- 7.1 (threshold = 7) 64 | load average 5min exceeds threshold in snapshot 'xxx' --- 6.1 (threshold = 6) 65 | load average 15min exceeds threshold in snapshot 'xxx' --- 5.1 (threshold = 5) 66 | memory swap size exceeds threshold in snapshot 'xxx' --- 1000001 KiB (threshold = 1000000 KiB) 67 | (5 rows) 68 | 69 | /**--- Alert the replication delay ---**/ 70 | UPDATE 1 71 | UPDATE 1 72 | INSERT 0 1 73 | alert 74 | ---------------------------------------------------------------------------------------------------------------------------- 75 | WAL flush-delay in '127.0.0.1:56442' exceeds threshold in snapshot 'xxx' --- 101 MiB (threshold = 100 MiB) 76 | replay-delay in '127.0.0.1:56442' exceeds threshold in snapshot 'xxx' --- 201 MiB (threshold = 200 MiB) 77 | (2 rows) 78 | 79 | /**--- Collect alert messages ---**/ 80 | snapid | message 81 | --------+----------------------------------- 82 | 2 | too many rollbacks in snapshot... 83 | 3 | too many transactions in snaps... 84 | 4 | Query average response time ex... 85 | 5 | Query worst response time exce... 86 | 6 | dead tuple size exceeds thresh... 87 | 6 | dead tuple ratio exceeds thres... 88 | 6 | dead tuple ratio in 'postgres.... 89 | 7 | correlation of the clustered t... 90 | 8 | too many backends in snapshot ... 91 | (9 rows) 92 | 93 | -------------------------------------------------------------------------------- /test/expected/function-command_option.out: -------------------------------------------------------------------------------- 1 | /*---- Initialize repository DB ----*/ 2 | /*---- Initialize monitored instance ----*/ 3 | /*---- Show snapshot list / Show snapshot size ----*/ 4 | /**--- Number of snapshots (0) ---**/ 5 | ---------------------------------------- 6 | Snapshot List 7 | ---------------------------------------- 8 | SnapshotID InstanceID Host Port Timestamp Comment Execute Time Size 9 | ----------------------------------------------------------------------------------------------------------------------------------------- 10 | exit: 0 11 | 12 | ---------------------------------------- 13 | Snapshot Size Information 14 | ---------------------------------------- 15 | Total Snapshot Size : 0 bytes 16 | 17 | exit: 0 18 | 19 | /**--- Number of snapshots (1) ---**/ 20 | ---------------------------------------- 21 | Snapshot List 22 | ---------------------------------------- 23 | SnapshotID InstanceID Host Port Timestamp Comment Execute Time Size 24 | ----------------------------------------------------------------------------------------------------------------------------------------- 25 | 1 1 statsinfo 5432 2012-11-01 00:00:00 1st 00:00:01 256 KiB 26 | exit: 0 27 | 28 | ---------------------------------------- 29 | Snapshot Size Information 30 | ---------------------------------------- 31 | Instance ID : 1 32 | Database System ID : 5807946214009601530 33 | Host : statsinfo 34 | Port : 5432 35 | Number Of Snapshots : 1 36 | Snapshot Size : 256 KiB 37 | Latest Snapshot ID : 1 38 | Latest Snapshot Timestamp : 2012-11-01 00:00:00 39 | 40 | Total Snapshot Size : 256 KiB 41 | 42 | exit: 0 43 | 44 | /**--- Number of snapshots (2) ---**/ 45 | ---------------------------------------- 46 | Snapshot List 47 | ---------------------------------------- 48 | SnapshotID InstanceID Host Port Timestamp Comment Execute Time Size 49 | ----------------------------------------------------------------------------------------------------------------------------------------- 50 | 1 1 statsinfo 5432 2012-11-01 00:00:00 1st 00:00:01 256 KiB 51 | 2 1 statsinfo 5432 2012-11-01 00:01:00 2nd 00:00:02 512 KiB 52 | exit: 0 53 | 54 | ---------------------------------------- 55 | Snapshot Size Information 56 | ---------------------------------------- 57 | Instance ID : 1 58 | Database System ID : 5807946214009601530 59 | Host : statsinfo 60 | Port : 5432 61 | Number Of Snapshots : 2 62 | Snapshot Size : 768 KiB 63 | Latest Snapshot ID : 2 64 | Latest Snapshot Timestamp : 2012-11-01 00:01:00 65 | 66 | Total Snapshot Size : 768 KiB 67 | 68 | exit: 0 69 | 70 | /**--- Specify the INSTANCEID that exists in data (list) ---**/ 71 | ---------------------------------------- 72 | Snapshot List 73 | ---------------------------------------- 74 | SnapshotID InstanceID Host Port Timestamp Comment Execute Time Size 75 | ----------------------------------------------------------------------------------------------------------------------------------------- 76 | 3 2 statsinfo 5433 2012-11-01 00:03:00 3rd 00:00:01 256 KiB 77 | exit: 0 78 | 79 | /**--- Specify the INSTANCEID that not exists in data (list) ---**/ 80 | ---------------------------------------- 81 | Snapshot List 82 | ---------------------------------------- 83 | SnapshotID InstanceID Host Port Timestamp Comment Execute Time Size 84 | ----------------------------------------------------------------------------------------------------------------------------------------- 85 | exit: 0 86 | 87 | /**--- There are multiple instances data (list) ---**/ 88 | ---------------------------------------- 89 | Snapshot Size Information 90 | ---------------------------------------- 91 | Instance ID : 1 92 | Database System ID : 5807946214009601530 93 | Host : statsinfo 94 | Port : 5432 95 | Number Of Snapshots : 2 96 | Snapshot Size : 768 KiB 97 | Latest Snapshot ID : 2 98 | Latest Snapshot Timestamp : 2012-11-01 00:01:00 99 | 100 | Instance ID : 2 101 | Database System ID : 5807946214009601531 102 | Host : statsinfo 103 | Port : 5433 104 | Number Of Snapshots : 1 105 | Snapshot Size : 256 KiB 106 | Latest Snapshot ID : 3 107 | Latest Snapshot Timestamp : 2012-11-01 00:03:00 108 | 109 | Total Snapshot Size : 1024 KiB 110 | 111 | exit: 0 112 | 113 | /*---- Get a snapshot ----*/ 114 | /**--- Specify the ASCII character as a comment of the snapshot ---**/ 115 | exit: 0 116 | 117 | /**--- Specify the UTF-8 character as a comment of the snapshot ---**/ 118 | exit: 0 119 | 120 | /**--- Specify the blank character as a comment of the snapshot ---**/ 121 | exit: 0 122 | 123 | /**--- Specify the empty character as a comment of the snapshot ---**/ 124 | exit: 0 125 | 126 | /*---- Delete a snapshot ----*/ 127 | /**--- Specify the INSTANCEID that exists in data ---**/ 128 | exit: 0 129 | 130 | /**--- Specify the INSTANCEID that not exists in data ---**/ 131 | exit: 0 132 | 133 | snapid | instid | comment 134 | --------+--------+---------------------------- 135 | 1 | 1 | "1st" 136 | 2 | 1 | "2nd" 137 | 4 | 3 | "COMMENT" 138 | 5 | 3 | "マルチバイト文字" 139 | 6 | 3 | " " 140 | 7 | 3 | "" 141 | (6 rows) 142 | 143 | /*---- pg_statsinfo's agent start / stop ----*/ 144 | /**--- Stop pg_statsinfo's agent ---**/ 145 | LOG: waiting for pg_statsinfod to shut down 146 | LOG: pg_statsinfod stopped 147 | exit: 0 148 | 149 | /**--- Start pg_statsinfo's agent ---**/ 150 | LOG: waiting for pg_statsinfod to start 151 | LOG: pg_statsinfod started 152 | exit: 0 153 | 154 | /*---- Quasi-normal pattern ----*/ 155 | /**--- Contain the snapshot that is same acquisition date ---**/ 156 | ---------------------------------------- 157 | Snapshot List 158 | ---------------------------------------- 159 | SnapshotID InstanceID Host Port Timestamp Comment Execute Time Size 160 | ----------------------------------------------------------------------------------------------------------------------------------------- 161 | 1 1 statsinfo 5432 2012-11-01 00:00:00 1st 00:00:01 256 KiB 162 | 2 1 statsinfo 5432 2012-11-01 00:00:00 2nd 00:00:02 512 KiB 163 | exit: 0 164 | 165 | ---------------------------------------- 166 | Snapshot Size Information 167 | ---------------------------------------- 168 | Instance ID : 1 169 | Database System ID : 5807946214009601530 170 | Host : statsinfo 171 | Port : 5432 172 | Number Of Snapshots : 2 173 | Snapshot Size : 768 KiB 174 | Latest Snapshot ID : 2 175 | Latest Snapshot Timestamp : 2012-11-01 00:00:00 176 | 177 | Total Snapshot Size : 768 KiB 178 | 179 | exit: 0 180 | 181 | -------------------------------------------------------------------------------- /test/expected/function-logger.out: -------------------------------------------------------------------------------- 1 | /*---- Initialize repository DB ----*/ 2 | /*---- Initialize monitored instance ----*/ 3 | CREATE TABLE 4 | CREATE FUNCTION 5 | /*---- Server log filter ----*/ 6 | /**--- Sets the textlog's filename and access permission ---**/ 7 | postgresql-statsinfo.log -rw-r--r--(644) 8 | server signaled 9 | pg_statsinfo.log -rw-rw-rw-(666) 10 | /**--- Textlog routing (textlog_min_messages = disable) ---**/ 11 | server signaled 12 | /**--- Textlog routing (textlog_min_messages = error) ---**/ 13 | server signaled 14 | 00000 LOG: textlog routing test (error) 15 | 00000 STATEMENT: SELECT statsinfo.elog('ALL', 'textlog routing test (error)') 16 | P0001 ERROR: textlog routing test (error) 17 | P0001 STATEMENT: SELECT statsinfo.elog('ALL', 'textlog routing test (error)') 18 | /**--- Textlog routing (adjust_log_level = off) ---**/ 19 | server signaled 20 | 42P01 ERROR: relation "xxx" does not exist at character 15 21 | 42P01 STATEMENT: SELECT * FROM xxx 22 | /**--- Adjust log level (adjust_log_info = '42P01') ---**/ 23 | server signaled 24 | 42P01 INFO: relation "xxx" does not exist at character 15 25 | 42P01 STATEMENT: SELECT * FROM xxx 26 | /**--- Adjust log level (adjust_log_notice = '42P01') ---**/ 27 | server signaled 28 | 42P01 NOTICE: relation "xxx" does not exist at character 15 29 | 42P01 STATEMENT: SELECT * FROM xxx 30 | /**--- Adjust log level (adjust_log_warning = '42P01') ---**/ 31 | server signaled 32 | 42P01 WARNING: relation "xxx" does not exist at character 15 33 | 42P01 STATEMENT: SELECT * FROM xxx 34 | /**--- Adjust log level (adjust_log_error = '00000') ---**/ 35 | server signaled 36 | 00000 ERROR: statement: SELECT 1 37 | /**--- Adjust log level (adjust_log_log = '42P01') ---**/ 38 | server signaled 39 | 42P01 LOG: relation "xxx" does not exist at character 15 40 | 42P01 STATEMENT: SELECT * FROM xxx 41 | /**--- Adjust log level (adjust_log_fatal = '42P01') ---**/ 42 | server signaled 43 | 42P01 FATAL: relation "xxx" does not exist at character 15 44 | 42P01 STATEMENT: SELECT * FROM xxx 45 | /**--- Sets the nologging filter (textlog_nologging_users = 'user01') ---**/ 46 | server signaled 47 | 00000 LOG: statement: SELECT 1 48 | /**--- Sets the nologging filter (textlog_nologging_users = 'user01, user02') ---**/ 49 | server signaled 50 | 00000 LOG: statement: SELECT 1 51 | /**--- Collect the CHECKPOINT information ---**/ 52 | CHECKPOINT 53 | INSERT 0 1 54 | instid | flags | start | num_buffers | xlog_added | xlog_removed | xlog_recycled | write_duration | sync_duration | total_duration 55 | --------+----------------------+-------+-------------+------------+--------------+---------------+----------------+---------------+---------------- 56 | 1 | immediate force wait | xxx | xxx | xxx | xxx | xxx | xxx | xxx | xxx 57 | 1 | time | xxx | xxx | xxx | xxx | xxx | xxx | xxx | xxx 58 | 1 | wal | xxx | xxx | xxx | xxx | xxx | xxx | xxx | xxx 59 | (3 rows) 60 | 61 | /**--- Collect the AUTOANALYZE information ---**/ 62 | server signaled 63 | INSERT 0 10000 64 | DELETE 4001 65 | instid | database | schema | table | start | duration 66 | --------+----------+--------+-------+-------+---------- 67 | 1 | postgres | public | tbl01 | xxx | xxx 68 | (1 row) 69 | 70 | /**--- Collect the AUTOVACUUM information ---**/ 71 | instid | database | schema | table | start | index_scans | page_removed | page_remain | tup_removed | tup_remain | tup_dead | page_hit | page_miss | page_dirty | read_rate | write_rate | duration 72 | --------+----------+--------+-------+-------+-------------+--------------+-------------+-------------+------------+----------+----------+-----------+------------+-----------+------------+---------- 73 | 1 | postgres | public | tbl01 | xxx | xxx | xxx | xxx | xxx | xxx | xxx | xxx | xxx | xxx | xxx | xxx | xxx 74 | (1 row) 75 | 76 | /**--- Collect the cancelled AUTOVACUUM information ---**/ 77 | server signaled 78 | TRUNCATE TABLE 79 | INSERT 0 100000 80 | DELETE 100000 81 | BEGIN 82 | LOCK TABLE 83 | instid | timestamp | database | schema | table | query 84 | --------+-----------+----------+--------+-------+------------------------- 85 | 1 | xxx | postgres | public | tbl01 | BEGIN; LOCK TABLE tbl01 86 | (1 row) 87 | 88 | -------------------------------------------------------------------------------- /test/expected/function-logstore.out: -------------------------------------------------------------------------------- 1 | /*---- Initialize repository DB ----*/ 2 | /*---- Initialize monitored instance ----*/ 3 | CREATE TABLE 4 | CREATE FUNCTION 5 | /*---- Server Log Accumulation ----*/ 6 | /**--- Log Storing (repolog_min_messages = disable) ---**/ 7 | server signaled 8 | elevel | message 9 | --------+--------- 10 | (0 rows) 11 | 12 | /**--- Log Storing (repolog_min_messages = error) ---**/ 13 | server signaled 14 | elevel | message 15 | --------+-------------------------- 16 | LOG | log storing test (error) 17 | ERROR | log storing test (error) 18 | (2 rows) 19 | 20 | /**--- Sets the nologging filter (repolog_nologging_users = 'user01') ---**/ 21 | server signaled 22 | elevel | username | message 23 | --------+----------+--------------------- 24 | LOG | user02 | statement: SELECT 1 25 | (1 row) 26 | 27 | /**--- Adjust log level (adjust_log_level = off) ---**/ 28 | server signaled 29 | elevel | sqlstate | message 30 | --------+----------+------------------------------- 31 | ERROR | 42P01 | relation "xxx" does not exist 32 | (1 row) 33 | 34 | /**--- Adjust log level (adjust_log_info = '42P01') ---**/ 35 | server signaled 36 | elevel | sqlstate | message 37 | --------+----------+------------------------------- 38 | INFO | 42P01 | relation "xxx" does not exist 39 | (1 row) 40 | 41 | /**--- Adjust log level (adjust_log_notice = '42P01') ---**/ 42 | server signaled 43 | elevel | sqlstate | message 44 | --------+----------+------------------------------- 45 | NOTICE | 42P01 | relation "xxx" does not exist 46 | (1 row) 47 | 48 | /**--- Adjust log level (adjust_log_warning = '42P01') ---**/ 49 | server signaled 50 | elevel | sqlstate | message 51 | ---------+----------+------------------------------- 52 | WARNING | 42P01 | relation "xxx" does not exist 53 | (1 row) 54 | 55 | /**--- Adjust log level (adjust_log_error = '00000') ---**/ 56 | server signaled 57 | elevel | sqlstate | message 58 | --------+----------+------------------------------------ 59 | ERROR | 00000 | statement: SELECT pg_backend_pid() 60 | (1 row) 61 | 62 | /**--- Adjust log level (adjust_log_log = '42P01') ---**/ 63 | server signaled 64 | elevel | sqlstate | message 65 | --------+----------+------------------------------- 66 | LOG | 42P01 | relation "xxx" does not exist 67 | (1 row) 68 | 69 | /**--- Adjust log level (adjust_log_fatal = '42P01') ---**/ 70 | server signaled 71 | elevel | sqlstate | message 72 | --------+----------+------------------------------- 73 | FATAL | 42P01 | relation "xxx" does not exist 74 | (1 row) 75 | 76 | -------------------------------------------------------------------------------- /test/expected/function-maintenance.out: -------------------------------------------------------------------------------- 1 | /*---- Initialize repository DB ----*/ 2 | /*---- Initialize monitored instance ----*/ 3 | /*---- Automatic maintenance function ----*/ 4 | /**--- Delete the snapshot for a certain period of time has elapsed ---**/ 5 | snapid | comment 6 | --------+------------ 7 | 1 | 2 days ago 8 | 2 | 1 days ago 9 | 3 | today 10 | (3 rows) 11 | 12 | UPDATE 1 13 | UPDATE 1 14 | server signaled 15 | snapid | comment 16 | --------+------------ 17 | 2 | 1 days ago 18 | 3 | today 19 | (2 rows) 20 | 21 | /**--- Server log maintenance ---**/ 22 | server signaled 23 | log maintenance command called 24 | -------------------------------------------------------------------------------- /test/expected/function-snapshot_replication.out: -------------------------------------------------------------------------------- 1 | /*---- Initialize repository DB ----*/ 2 | /*---- Initialize monitored instance (replication configuration) ----*/ 3 | /*---- Initialize logical standby instance ----*/ 4 | NOTICE: created replication slot "sub" on publisher 5 | /***-- Statistics of WAL (MASTER) --***/ 6 | snapid | location | xlogfile 7 | --------+----------+---------- 8 | 1 | xxx | xxx 9 | (1 row) 10 | 11 | /***-- Statistics of archive (MASTER) --***/ 12 | snapid | archived_count | last_archived_wal | last_archived_time | failed_count | last_failed_wal | last_failed_time | stats_reset 13 | --------+----------------+-------------------+--------------------+--------------+-----------------+------------------+------------- 14 | 2 | 1 | xxx | xxx | 0 | | | xxx 15 | (1 row) 16 | 17 | /***-- Statistics of replication (MASTER) --***/ 18 | snapid | procpid | usesysid | usename | application_name | client_addr | client_hostname | client_port | backend_start | backend_xmin | state | current_location | sent_location | write_location | flush_location | replay_location | write_lag | flush_lag | replay_lag | sync_priority | sync_state 19 | --------+---------+----------+----------+------------------+-------------+-----------------+-------------+---------------+--------------+-----------+------------------+---------------+----------------+----------------+-----------------+-----------+-----------+------------+---------------+------------ 20 | 2 | xxx | xxx | postgres | sub | xxx | xxx | xxx | xxx | | streaming | xxx | xxx | xxx | xxx | xxx | xxx | xxx | xxx | 0 | async 21 | 2 | xxx | xxx | postgres | walreceiver | xxx | xxx | xxx | xxx | xxx | streaming | xxx | xxx | xxx | xxx | xxx | xxx | xxx | xxx | 0 | async 22 | (2 rows) 23 | 24 | /***-- Statistics of replication slot (MASTER) --***/ 25 | snapid | slot_name | plugin | slot_type | datoid | temporary | active | active_pid | xact_xmin | catalog_xmin | restart_lsn | confirmed_flush_lsn 26 | --------+-----------+----------+-----------+--------+-----------+--------+------------+-----------+--------------+-------------+--------------------- 27 | 2 | sub | pgoutput | logical | xxx | f | t | xxx | | xxx | xxx | xxx 28 | (1 row) 29 | 30 | /***-- Statistics of WAL (STANDBY) --***/ 31 | snapid | location | xlogfile 32 | --------+----------+---------- 33 | (0 rows) 34 | 35 | /***-- Statistics of archive (STANDBY) --***/ 36 | snapid | archived_count | last_archived_wal | last_archived_time | failed_count | last_failed_wal | last_failed_time | stats_reset 37 | --------+----------------+-------------------+--------------------+--------------+-----------------+------------------+------------- 38 | 3 | 0 | | | 0 | | | xxx 39 | (1 row) 40 | 41 | /***-- Statistics of replication (STANDBY) --***/ 42 | snapid | procpid | usesysid | usename | application_name | client_addr | client_hostname | client_port | backend_start | backend_xmin | state | current_location | sent_location | write_location | flush_location | replay_location | write_lag | flush_lag | replay_lag | sync_priority | sync_state 43 | --------+---------+----------+---------+------------------+-------------+-----------------+-------------+---------------+--------------+-------+------------------+---------------+----------------+----------------+-----------------+-----------+-----------+------------+---------------+------------ 44 | (0 rows) 45 | 46 | -------------------------------------------------------------------------------- /test/regress.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SCRIPT_DIR="./script" 4 | RESULTS_DIR="./results" 5 | EXPECTED_DIR="./expected" 6 | 7 | function verify_libraries() 8 | { 9 | local pkglibdir=$(pg_config --pkglibdir) 10 | local failed=0 11 | 12 | if [ -f "${pkglibdir}/pg_statsinfo.so" -o \ 13 | -f "${pkglibdir}/pgsql/pg_statsinfo.so" ] ; then 14 | printf "%-35s ... ok\n" "pg_statsinfo" 15 | else 16 | printf "%-35s ... not installed\n" "pg_statsinfo" 17 | failed=1 18 | fi 19 | 20 | if [ -f "${pkglibdir}/pg_stat_statements.so" -o \ 21 | -f "${pkglibdir}/pgsql/pg_stat_statements.so" ] ; then 22 | printf "%-35s ... ok\n" "pg_stat_statements" 23 | else 24 | printf "%-35s ... not installed\n" "pg_stat_statements" 25 | failed=1 26 | fi 27 | 28 | if [ ${failed} -eq 1 ] ; then 29 | exit 1 30 | fi 31 | } 32 | 33 | function do_test() 34 | { 35 | local regress_list=("${@}") 36 | 37 | for regress in "${regress_list[@]}" 38 | do 39 | local script="${SCRIPT_DIR}/${regress}.sh" 40 | local result="${RESULTS_DIR}/${regress}.out" 41 | local expect="${EXPECTED_DIR}/${regress}.out" 42 | local diff="${RESULTS_DIR}/${regress}.diff" 43 | local ret="FAILED" 44 | 45 | printf "test %-30s ... " "${regress}" 46 | 47 | ( eval "${script}" > "${result}" 2>&1 ) 48 | 49 | diff "${result}" "${expect}" > ${diff} 50 | 51 | if [ ${?} -eq 0 ] ; then 52 | ret="ok" 53 | success=$(expr ${success} + 1) 54 | fi 55 | echo "${ret}" 56 | done 57 | } 58 | 59 | regress_list=("${@}") 60 | total=${#regress_list[@]} 61 | success=0 62 | 63 | which pg_config > /dev/null 2>&1 64 | 65 | if [ ${?} -ne 0 ] ; then 66 | echo "ERROR: pg_config is not in the path" 1>&2 67 | exit 1 68 | fi 69 | 70 | echo "=== system information =============================================" 71 | uname -a 72 | lsb_release -a 2> /dev/null 73 | 74 | echo "=== verify the required libraries are installed ====================" 75 | verify_libraries 76 | 77 | echo "=== cleanup working directory ======================================" 78 | rm -fr ${RESULTS_DIR} && mkdir -pv ${RESULTS_DIR} 79 | 80 | echo "=== running regression test scripts ================================" 81 | do_test "${regress_list[@]}" 82 | 83 | cat << __EOF__ 84 | === regression test finish ========================================= 85 | 86 | ======================== 87 | ${success} of ${total} tests passed. 88 | ======================== 89 | 90 | __EOF__ 91 | 92 | if [ ${success} -eq ${total} ] ; then 93 | exit 0 94 | else 95 | exit 1 96 | fi 97 | -------------------------------------------------------------------------------- /test/script/common.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | BASE_DIR=$(pwd) 4 | DBCLUSTER_DIR=${BASE_DIR}/results/dbcluster 5 | CONFIG_DIR=${BASE_DIR}/script/config 6 | INPUTDATA_DIR=${BASE_DIR}/script/inputdata 7 | 8 | export PGDATA=${DBCLUSTER_DIR}/pgdata 9 | export PGPORT=57400 10 | export PGUSER=postgres 11 | export PGDATABASE=postgres 12 | export PGTZ=JST-9 13 | export PGDATESTYLE='ISO, MDY' 14 | export LANG=C 15 | 16 | REPOSITORY_DATA=${DBCLUSTER_DIR}/repository 17 | REPOSITORY_CONFIG=${CONFIG_DIR}/postgresql-repository.conf 18 | REPOSITORY_USER=postgres 19 | REPOSITORY_PORT=57500 20 | 21 | function server_version() 22 | { 23 | local version="" 24 | local version_num=0 25 | local token_num=0 26 | local vmaj=0 27 | local vmin=0 28 | local vrev=0 29 | 30 | version=$(postgres --version | sed 's/postgres\s(PostgreSQL)\s//') 31 | token_num=$(echo ${version} | grep -o '\.' | wc -l) 32 | if [ ${token_num} -eq 2 ] ; then 33 | vmaj=$(echo ${version} | cut -d '.' -f 1) 34 | vmin=$(echo ${version} | cut -d '.' -f 2) 35 | vrev=$(echo ${version} | cut -d '.' -f 3) 36 | elif [ ${token_num} -eq 1 ] ; then 37 | vmaj=$(echo ${version} | cut -d '.' -f 1) 38 | if [ ${vmaj} -ge 10 ] ; then 39 | vrev=$(echo ${version} | cut -d '.' -f 2) 40 | else 41 | vmin=$(echo ${version} | cut -d '.' -f 2 | sed 's/\([0-9]\+\).*/\1/') 42 | fi 43 | else 44 | vmaj=$(echo ${version} | cut -d '.' -f 1 | sed 's/\([0-9]\+\).*/\1/') 45 | fi 46 | 47 | version_num=$(expr \( 100 \* ${vmaj} + ${vmin} \) \* 100 + ${vrev}) 48 | echo ${version_num} 49 | } 50 | 51 | if [ $(server_version) -ge 100000 ] ; then 52 | PGLOG_DIR=${PGDATA}/log 53 | FUNCTION_PG_SWITCH_WAL="pg_switch_wal()" 54 | else 55 | PGLOG_DIR=${PGDATA}/pg_log 56 | FUNCTION_PG_SWITCH_WAL="pg_switch_xlog()" 57 | fi 58 | 59 | function setup_repository() 60 | { 61 | local datadir=${1} 62 | local superuser=${2} 63 | local port=${3} 64 | local pgconfig=${4} 65 | 66 | pg_ctl stop -m immediate -D ${datadir} > /dev/null 2>&1 67 | rm -fr ${datadir} 68 | 69 | initdb --no-locale -U ${superuser} -D ${datadir} > /dev/null 2>&1 70 | if [ ${?} -ne 0 ] ; then 71 | echo "ERROR: could not create database cluster of repository" 1>&2 72 | exit 1 73 | fi 74 | 75 | set_pgconfig ${pgconfig} ${datadir} 76 | 77 | pg_ctl start -w -D ${datadir} -o "-p ${port}" > /dev/null 78 | if [ ${?} -ne 0 ] ; then 79 | echo "ERROR: could not start database cluster of repository" 1>&2 80 | exit 1 81 | fi 82 | } 83 | 84 | function setup_dbcluster() 85 | { 86 | local datadir=${1} 87 | local superuser=${2} 88 | local port=${3} 89 | local pgconfig=${4} 90 | local archivedir=${5} 91 | local xlogdir=${6} 92 | local hbaconfig=${7} 93 | local initdb_cmd="" 94 | 95 | pg_ctl stop -m immediate -D ${datadir} > /dev/null 2>&1 96 | rm -fr ${datadir} 97 | 98 | initdb_cmd="initdb --no-locale" 99 | [ ! -z ${datadir} ] && 100 | initdb_cmd="${initdb_cmd} -D ${datadir}" 101 | [ ! -z ${superuser} ] && 102 | initdb_cmd="${initdb_cmd} -U ${superuser}" 103 | [ ! -z ${xlogdir} ] && 104 | initdb_cmd="${initdb_cmd} -X ${xlogdir}" 105 | 106 | eval ${initdb_cmd} > /dev/null 2>&1 107 | if [ ${?} -ne 0 ] ; then 108 | echo "ERROR: could not create database cluster of statsinfo" 1>&2 109 | exit 1 110 | fi 111 | 112 | [ ! -z ${archivedir} ] && 113 | rm -fr ${archivedir} && mkdir -p ${archivedir} 114 | 115 | set_pgconfig ${pgconfig} ${datadir} ${archivedir} 116 | 117 | [ ! -z ${hbaconfig} ] && 118 | cp ${hbaconfig} ${PGDATA_ACT}/pg_hba.conf 119 | 120 | if [ ! -z ${port} ] ; then 121 | pg_ctl start -w -D ${datadir} -o "-p ${port}" > /dev/null 122 | else 123 | pg_ctl start -w -D ${datadir} > /dev/null 124 | fi 125 | if [ ${?} -ne 0 ] ; then 126 | echo "ERROR: could not start database cluster of statsinfo" 1>&2 127 | exit 1 128 | fi 129 | } 130 | 131 | function set_pgconfig() 132 | { 133 | local pgconfig=${1} 134 | local datadir=${2} 135 | local archivedir=${3} 136 | 137 | grep -q "include 'postgresql-statsinfo.conf'" ${datadir}/postgresql.conf 138 | [ ${?} -ne 0 ] && 139 | echo "include 'postgresql-statsinfo.conf'" >> ${datadir}/postgresql.conf 140 | 141 | if [ -z ${pgconfig} ] ; then 142 | touch ${datadir}/postgresql-statsinfo.conf 143 | else 144 | local version=$(server_version) 145 | local guc_prefix="pg_statsinfo" 146 | local buffer="" 147 | 148 | buffer=$(cat ${pgconfig}) 149 | if [ ${?} -ne 0 ] ; then 150 | echo "ERROR: could not read statsinfo's setting base file" 1>&2 151 | exit 1 152 | fi 153 | 154 | if [ ${version} -ge 90200 ] ; then 155 | buffer=$(echo "${buffer}" | grep -Pv "^\s*custom_variable_classes\s*=") 156 | fi 157 | 158 | if [ ${version} -lt 90200 ] ; then 159 | buffer=$(echo "${buffer}" | grep -Pv "^\s*track_io_timing\s*=") 160 | fi 161 | 162 | if [ ${version} -lt 90000 ] ; then 163 | buffer=$(echo "${buffer}" | grep -Pv "^\s*wal_level\s*=") 164 | fi 165 | 166 | echo "${buffer}" | 167 | sed "s##${archivedir}#" | 168 | sed "s//${guc_prefix}/" | 169 | sed "s//${REPOSITORY_PORT}/" | 170 | sed "s//${REPOSITORY_USER}/" > ${datadir}/postgresql-statsinfo.conf 171 | fi 172 | if [ ${?} -ne 0 ] ; then 173 | echo "ERROR: could not write statsinfo's setting to config file" 1>&2 174 | exit 1 175 | fi 176 | } 177 | 178 | function update_pgconfig() 179 | { 180 | local datadir=${1} 181 | local param=${2} 182 | local value=${3} 183 | local buffer="" 184 | 185 | param=$(echo "${param}" | sed "s//pg_statsinfo/") 186 | 187 | grep -Pq "^\s*${param}\s*=" ${datadir}/postgresql-statsinfo.conf 188 | if [ ${?} -ne 0 ] ; then 189 | echo "${param} = ${value}" >> ${datadir}/postgresql-statsinfo.conf 190 | return 191 | fi 192 | 193 | buffer=$(sed "s/^\s*${param}\s*=.\+/${param} = ${value}/" ${datadir}/postgresql-statsinfo.conf) 194 | echo "${buffer}" > ${datadir}/postgresql-statsinfo.conf 195 | } 196 | 197 | function delete_pgconfig() 198 | { 199 | local datadir=${1} 200 | local param=${2} 201 | local buffer="" 202 | 203 | param=$(echo "${param}" | sed "s//pg_statsinfo/") 204 | 205 | buffer=$(grep -Pv "^\s*${param}\s*=.\+" ${datadir}/postgresql-statsinfo.conf) 206 | echo "${buffer}" > ${datadir}/postgresql-statsinfo.conf 207 | } 208 | 209 | function do_snapshot() 210 | { 211 | local user=${1} 212 | local port=${2} 213 | local repodb_user=${3} 214 | local repodb_port=${4} 215 | local comment=${5:-""} 216 | local prev_count=0 217 | local curr_count=0 218 | local retry=0 219 | 220 | prev_count=$(psql -U ${repodb_user} -p ${repodb_port} -d postgres -Atc "SELECT count(*) FROM statsrepo.snapshot") 221 | if [ ${?} -ne 0 ] ; then 222 | echo "ERROR: could not get snapid from repository" 1>&2 223 | exit 1 224 | fi 225 | 226 | pg_statsinfo -U ${user} -p ${port} -d postgres -S "${comment}" 227 | 228 | while [ ${retry} -lt 30 ] 229 | do 230 | curr_count=$(psql -U ${repodb_user} -p ${repodb_port} -d postgres -Atc "SELECT count(*) FROM statsrepo.snapshot") 231 | if [ ${?} -ne 0 ] ; then 232 | echo "ERROR: could not get snapid from repository" 1>&2 233 | exit 1 234 | fi 235 | 236 | [ ${curr_count} -gt ${prev_count} ] && 237 | return 238 | 239 | retry=$(expr ${retry} + 1) 240 | sleep 1 241 | done 242 | 243 | echo "ERROR: snapshot has timeout" 1>&2 244 | exit 1 245 | } 246 | 247 | function send_query() 248 | { 249 | psql -U ${REPOSITORY_USER} -p ${REPOSITORY_PORT} -d postgres "${@}" <&0 250 | } 251 | 252 | function exec_command() 253 | { 254 | eval "${1}" 255 | printf "exit: %d\n\n" ${?} 256 | } 257 | 258 | function stop_all_database() 259 | { 260 | if [ -e ${DBCLUSTER_DIR} ] ; then 261 | for datadir in $(find ${DBCLUSTER_DIR} -maxdepth 1 -mindepth 1 -type d) 262 | do 263 | pg_ctl stop -m fast -D ${datadir} > /dev/null 2>&1 264 | done 265 | fi 266 | } 267 | -------------------------------------------------------------------------------- /test/script/config/pg_hba-replication.conf: -------------------------------------------------------------------------------- 1 | # TYPE DATABASE USER ADDRESS METHOD 2 | # "local" is for Unix domain socket connections only 3 | local all all trust 4 | # IPv4 local connections: 5 | host all all 127.0.0.1/32 trust 6 | # IPv6 local connections: 7 | host all all ::1/128 trust 8 | # Allow replication connections from localhost, by a user with the replication privilege 9 | host replication all 127.0.0.1/32 trust 10 | -------------------------------------------------------------------------------- /test/script/config/postgresql-alert.conf: -------------------------------------------------------------------------------- 1 | shared_preload_libraries = 'pg_statsinfo, pg_stat_statements' 2 | log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log' 3 | autovacuum = off 4 | custom_variable_classes = '' 5 | .snapshot_interval = 2147483647 6 | .repository_server = 'port= user=' 7 | .textlog_min_messages = alert 8 | .textlog_line_prefix = '' 9 | .enable_alert = on 10 | -------------------------------------------------------------------------------- /test/script/config/postgresql-logger.conf: -------------------------------------------------------------------------------- 1 | shared_preload_libraries = 'pg_statsinfo' 2 | log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log' 3 | autovacuum = off 4 | log_min_messages = info 5 | custom_variable_classes = '' 6 | .snapshot_interval = 2147483647 7 | .repository_server = 'port= user=' 8 | .textlog_min_messages = info 9 | .textlog_line_prefix = '%e ' 10 | -------------------------------------------------------------------------------- /test/script/config/postgresql-logical-sby.conf: -------------------------------------------------------------------------------- 1 | listen_addresses = '*' 2 | wal_level = logical 3 | -------------------------------------------------------------------------------- /test/script/config/postgresql-logstore.conf: -------------------------------------------------------------------------------- 1 | shared_preload_libraries = 'pg_statsinfo' 2 | log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log' 3 | autovacuum = off 4 | log_min_messages = info 5 | custom_variable_classes = '' 6 | .snapshot_interval = 2147483647 7 | .repository_server = 'port= user=' 8 | .repolog_min_messages = info 9 | .repolog_interval = 0 10 | -------------------------------------------------------------------------------- /test/script/config/postgresql-maintenance.conf: -------------------------------------------------------------------------------- 1 | shared_preload_libraries = 'pg_statsinfo' 2 | log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log' 3 | autovacuum = off 4 | custom_variable_classes = '' 5 | .snapshot_interval = 2147483647 6 | .repository_server = 'port= user=' 7 | -------------------------------------------------------------------------------- /test/script/config/postgresql-replication-act.conf: -------------------------------------------------------------------------------- 1 | listen_addresses = '*' 2 | log_hostname = on 3 | wal_level = logical 4 | archive_mode = on 5 | archive_command = 'cp %p /%f' 6 | shared_preload_libraries = 'pg_statsinfo' 7 | log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log' 8 | track_functions = 'all' 9 | autovacuum = off 10 | .snapshot_interval = 2147483647 11 | .long_lock_threshold = 0 12 | .repository_server = 'port= user=' 13 | -------------------------------------------------------------------------------- /test/script/config/postgresql-replication-sby.conf: -------------------------------------------------------------------------------- 1 | listen_addresses = '*' 2 | hot_standby = on 3 | hot_standby_feedback = on 4 | shared_preload_libraries = 'pg_statsinfo' 5 | log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log' 6 | track_functions = 'all' 7 | autovacuum = off 8 | .snapshot_interval = 2147483647 9 | .long_lock_threshold = 0 10 | .repository_server = 'port= user=' 11 | -------------------------------------------------------------------------------- /test/script/config/postgresql-report.conf: -------------------------------------------------------------------------------- 1 | shared_preload_libraries = 'pg_statsinfo' 2 | log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log' 3 | track_functions = 'all' 4 | log_checkpoints = on 5 | autovacuum = off 6 | custom_variable_classes = '' 7 | .snapshot_interval = 2147483647 8 | .repository_server = 'port= user=' 9 | .repolog_min_messages = disable 10 | -------------------------------------------------------------------------------- /test/script/config/postgresql-repository.conf: -------------------------------------------------------------------------------- 1 | logging_collector = on 2 | -------------------------------------------------------------------------------- /test/script/config/postgresql-snapshot.conf: -------------------------------------------------------------------------------- 1 | shared_buffers = 32MB 2 | shared_preload_libraries = 'pg_statsinfo' 3 | log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log' 4 | track_functions = 'all' 5 | autovacuum = off 6 | archive_mode = on 7 | archive_command = 'cp %p /%f' 8 | wal_level = archive 9 | custom_variable_classes = '' 10 | .snapshot_interval = 2147483647 11 | .long_lock_threshold = 0 12 | .repository_server = 'port= user=' 13 | -------------------------------------------------------------------------------- /test/script/function-alert.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | . ./script/common.sh 4 | 5 | PGCONFIG=${CONFIG_DIR}/postgresql-alert.conf 6 | 7 | RELOAD_DELAY=3 8 | ANALYZE_DELAY=1 9 | SAMPLING_DELAY=6 10 | WRITE_DELAY=1 11 | 12 | function get_snapshot() 13 | { 14 | do_snapshot ${PGUSER} ${PGPORT} ${REPOSITORY_USER} ${REPOSITORY_PORT} 15 | } 16 | 17 | trap stop_all_database EXIT 18 | 19 | echo "/*---- Initialize repository DB ----*/" 20 | setup_repository ${REPOSITORY_DATA} ${REPOSITORY_USER} ${REPOSITORY_PORT} ${REPOSITORY_CONFIG} 21 | 22 | echo "/*---- Initialize monitored instance ----*/" 23 | setup_dbcluster ${PGDATA} ${PGUSER} ${PGPORT} ${PGCONFIG} "" "" "" 24 | sleep 3 25 | psql -c "CREATE EXTENSION pg_stat_statements" > /dev/null 26 | 27 | get_snapshot 28 | send_query << EOF > /dev/null 29 | UPDATE statsrepo.alert SET rollback_tps = -1; 30 | UPDATE statsrepo.alert SET commit_tps = -1; 31 | UPDATE statsrepo.alert SET garbage_size = -1; 32 | UPDATE statsrepo.alert SET garbage_percent = -1; 33 | UPDATE statsrepo.alert SET garbage_percent_table = -1; 34 | UPDATE statsrepo.alert SET response_avg = -1; 35 | UPDATE statsrepo.alert SET response_worst = -1; 36 | UPDATE statsrepo.alert SET backend_max = -1; 37 | UPDATE statsrepo.alert SET correlation_percent = -1; 38 | UPDATE statsrepo.alert SET disk_remain_percent = -1; 39 | UPDATE statsrepo.alert SET loadavg_1min = -1; 40 | UPDATE statsrepo.alert SET loadavg_5min = -1; 41 | UPDATE statsrepo.alert SET loadavg_15min = -1; 42 | UPDATE statsrepo.alert SET swap_size = -1; 43 | UPDATE statsrepo.alert SET rep_flush_delay = -1; 44 | UPDATE statsrepo.alert SET rep_replay_delay = -1; 45 | EOF 46 | 47 | echo "/*---- Alert Function ----*/" 48 | echo "/**--- Alert the number of rollbacks per second ---**/" 49 | send_query -c "UPDATE statsrepo.alert SET rollback_tps = 0" 50 | psql << EOF 51 | BEGIN; 52 | CREATE TABLE tbl01 (id bigint); 53 | ROLLBACK; 54 | ANALYZE; 55 | EOF 56 | sleep ${ANALYZE_DELAY} 57 | get_snapshot 58 | sleep ${WRITE_DELAY} 59 | tail -n 1 ${PGLOG_DIR}/pg_statsinfo.log | 60 | sed "s/[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}\s[0-9]\{2\}:[0-9]\{2\}:[0-9]\{2\}/xxx/g" | 61 | sed "s#--- .\+ Rollbacks/sec #--- xxx Rollbacks/sec #" 62 | send_query -c "UPDATE statsrepo.alert SET rollback_tps = -1" > /dev/null 63 | 64 | echo "/**--- Alert the number of commits per second ---**/" 65 | send_query -c "UPDATE statsrepo.alert SET commit_tps = 0" 66 | psql << EOF 67 | BEGIN; 68 | CREATE TABLE tbl01 (id bigint); 69 | COMMIT; 70 | ANALYZE; 71 | EOF 72 | sleep ${ANALYZE_DELAY} 73 | get_snapshot 74 | sleep ${WRITE_DELAY} 75 | tail -n 1 ${PGLOG_DIR}/pg_statsinfo.log | 76 | sed "s/[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}\s[0-9]\{2\}:[0-9]\{2\}:[0-9]\{2\}/xxx/g" | 77 | sed "s#--- .\+ Transactions/sec #--- xxx Transactions/sec #" 78 | send_query -c "UPDATE statsrepo.alert SET commit_tps = -1" > /dev/null 79 | 80 | echo "/**--- Alert the response time average of query ---**/" 81 | send_query -c "UPDATE statsrepo.alert SET response_avg = 0" 82 | psql -c "SELECT pg_sleep(1)" > /dev/null 83 | get_snapshot 84 | sleep ${WRITE_DELAY} 85 | tail -n 1 ${PGLOG_DIR}/pg_statsinfo.log | 86 | sed "s/[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}\s[0-9]\{2\}:[0-9]\{2\}:[0-9]\{2\}/xxx/g" | 87 | sed "s/--- .\+ sec /--- xxx sec /" 88 | send_query -c "UPDATE statsrepo.alert SET response_avg = -1" > /dev/null 89 | 90 | echo "/**--- Alert the response time max of query ---**/" 91 | send_query -c "UPDATE statsrepo.alert SET response_worst = 0" 92 | psql -c "SELECT pg_sleep(1)" > /dev/null 93 | get_snapshot 94 | sleep ${WRITE_DELAY} 95 | tail -n 1 ${PGLOG_DIR}/pg_statsinfo.log | 96 | sed "s/[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}\s[0-9]\{2\}:[0-9]\{2\}:[0-9]\{2\}/xxx/g" | 97 | sed "s/--- .\+ sec /--- xxx sec /" 98 | send_query -c "UPDATE statsrepo.alert SET response_worst = -1" > /dev/null 99 | 100 | echo "/**--- Alert the dead tuple size and ratio ---**/" 101 | send_query << EOF 102 | UPDATE statsrepo.alert SET garbage_size = 0; 103 | UPDATE statsrepo.alert SET garbage_percent = 30; 104 | UPDATE statsrepo.alert SET garbage_percent_table = 60; 105 | EOF 106 | psql << EOF 107 | CREATE TABLE tbl02 (id bigint); 108 | CREATE TABLE tbl03 (id bigint); 109 | INSERT INTO tbl02 VALUES (generate_series(1,500000)); 110 | INSERT INTO tbl03 VALUES (generate_series(1,500000)); 111 | DELETE FROM tbl02 WHERE id <= 400000; 112 | DELETE FROM tbl03 WHERE id <= 300000; 113 | EOF 114 | sleep 1 115 | psql -c "ANALYZE" 116 | sleep ${ANALYZE_DELAY} 117 | get_snapshot 118 | sleep ${WRITE_DELAY} 119 | tail -n 3 ${PGLOG_DIR}/pg_statsinfo.log | 120 | sed "s/[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}\s[0-9]\{2\}:[0-9]\{2\}:[0-9]\{2\}/xxx/g" | 121 | sed "s/--- .\+ \(MiB\|%\) /--- xxx \1 /" 122 | send_query << EOF > /dev/null 123 | UPDATE statsrepo.alert SET garbage_size = -1; 124 | UPDATE statsrepo.alert SET garbage_percent = -1; 125 | UPDATE statsrepo.alert SET garbage_percent_table = -1; 126 | EOF 127 | 128 | echo "/**--- Alert the correlation of table ---**/" 129 | send_query -c "UPDATE statsrepo.alert SET correlation_percent = 100" 130 | psql << EOF 131 | SET client_min_messages TO warning; 132 | CREATE TABLE tbl04 (id bigint PRIMARY KEY); 133 | ALTER TABLE tbl04 CLUSTER ON tbl04_pkey; 134 | INSERT INTO tbl04 VALUES (5); 135 | INSERT INTO tbl04 VALUES (4); 136 | INSERT INTO tbl04 VALUES (1); 137 | INSERT INTO tbl04 VALUES (3); 138 | INSERT INTO tbl04 VALUES (2); 139 | ANALYZE; 140 | EOF 141 | sleep ${ANALYZE_DELAY} 142 | get_snapshot 143 | sleep ${WRITE_DELAY} 144 | tail -n 1 ${PGLOG_DIR}/pg_statsinfo.log | 145 | sed "s/[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}\s[0-9]\{2\}:[0-9]\{2\}:[0-9]\{2\}/xxx/g" 146 | send_query -c "UPDATE statsrepo.alert SET correlation_percent = -1" > /dev/null 147 | 148 | echo "/**--- Alert the number of backend processes ---**/" 149 | send_query -c "UPDATE statsrepo.alert SET backend_max = 0" 150 | psql -c "SELECT pg_sleep(${SAMPLING_DELAY})" > /dev/null 151 | get_snapshot 152 | sleep ${WRITE_DELAY} 153 | tail -n 1 ${PGLOG_DIR}/pg_statsinfo.log | 154 | sed "s/[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}\s[0-9]\{2\}:[0-9]\{2\}:[0-9]\{2\}/xxx/g" 155 | send_query -c "UPDATE statsrepo.alert SET backend_max = -1" > /dev/null 156 | 157 | echo "/**--- Alert the condition of the OS resource ---**/" 158 | send_query << EOF 159 | UPDATE statsrepo.alert SET disk_remain_percent = default; 160 | UPDATE statsrepo.alert SET (loadavg_1min, loadavg_5min, loadavg_15min) = (default, default, default); 161 | UPDATE statsrepo.alert SET swap_size = default; 162 | UPDATE statsrepo.tablespace SET (avail, total) = (1.9*1024^3, 10*1024^3) WHERE name = 'pg_default' AND snapid = 2; 163 | UPDATE statsrepo.loadavg SET (loadavg1, loadavg5, loadavg15) = (7.1, 6.1, 5.1) WHERE snapid = 2; 164 | UPDATE statsrepo.memory SET swap = 1000001 WHERE snapid = 2; 165 | EOF 166 | send_query -c "SELECT * FROM statsrepo.alert(2)" | 167 | sed "s/[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}\s[0-9]\{2\}:[0-9]\{2\}:[0-9]\{2\}/xxx/g" 168 | send_query << EOF > /dev/null 169 | UPDATE statsrepo.alert SET disk_remain_percent = -1; 170 | UPDATE statsrepo.alert SET (loadavg_1min, loadavg_5min, loadavg_15min) = (-1, -1, -1); 171 | UPDATE statsrepo.alert SET swap_size = -1; 172 | EOF 173 | 174 | echo "/**--- Alert the replication delay ---**/" 175 | send_query << EOF 176 | UPDATE statsrepo.alert SET rep_flush_delay = default; 177 | UPDATE statsrepo.alert SET rep_replay_delay = default; 178 | INSERT INTO statsrepo.replication VALUES ( 179 | 2, 2673, 10, 'postgres', 'sby', '127.0.0.1', NULL, 56442, '2013-01-01 00:00:00', '100', 'streaming', 180 | '0/C900000 (00000001000000000000000C)', '0/6400000 (000000010000000000000006)', 181 | '0/6400000 (000000010000000000000006)', '0/6400000 (000000010000000000000006)', 182 | '0/0 (000000010000000000000000)', '00:01:00', '00:02:00', '00:03:00', 0, 'sync'); 183 | EOF 184 | send_query -c "SELECT * FROM statsrepo.alert(2)" | 185 | sed "s/[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}\s[0-9]\{2\}:[0-9]\{2\}:[0-9]\{2\}/xxx/g" 186 | send_query << EOF > /dev/null 187 | UPDATE statsrepo.alert SET rep_flush_delay = -1; 188 | UPDATE statsrepo.alert SET rep_replay_delay = -1; 189 | EOF 190 | 191 | echo "/**--- Collect alert messages ---**/" 192 | send_query -c "SELECT snapid, substr(message, 1, 30) || '...' AS message FROM statsrepo.alert_message" 193 | -------------------------------------------------------------------------------- /test/script/function-command_option.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | . ./script/common.sh 4 | 5 | PGCONFIG=${CONFIG_DIR}/postgresql-report.conf 6 | 7 | SNAPSHOT_DELAY=3 8 | 9 | function exec_statsinfo() 10 | { 11 | pg_statsinfo -U ${REPOSITORY_USER} -p ${REPOSITORY_PORT} "${@}" 12 | } 13 | 14 | function exec_statsinfo2() 15 | { 16 | pg_statsinfo -U ${PGUSER} -p ${PGPORT} "${@}" 17 | } 18 | 19 | trap stop_all_database EXIT 20 | 21 | export PGOPTIONS=' -c intervalstyle=postgres' 22 | 23 | echo "/*---- Initialize repository DB ----*/" 24 | setup_repository ${REPOSITORY_DATA} ${REPOSITORY_USER} ${REPOSITORY_PORT} ${REPOSITORY_CONFIG} 25 | 26 | echo "/*---- Initialize monitored instance ----*/" 27 | setup_dbcluster ${PGDATA} ${PGUSER} ${PGPORT} ${PGCONFIG} "" "" "" 28 | sleep 3 29 | 30 | send_query -c "DELETE FROM statsrepo.instance" > /dev/null 31 | 32 | echo "/*---- Show snapshot list / Show snapshot size ----*/" 33 | echo "/**--- Number of snapshots (0) ---**/" 34 | exec_command "exec_statsinfo -l" 35 | exec_command "exec_statsinfo -s" 36 | 37 | echo "/**--- Number of snapshots (1) ---**/" 38 | send_query << EOF > /dev/null 39 | DELETE FROM statsrepo.snapshot; 40 | DELETE FROM statsrepo.instance; 41 | INSERT INTO statsrepo.instance VALUES (1, '5807946214009601530', 'statsinfo', '5432', '8.3.0'); 42 | INSERT INTO statsrepo.snapshot VALUES (1, 1, '2012-11-01 00:00:00+09', '1st', '00:00:01', 262144); 43 | EOF 44 | exec_command "exec_statsinfo -l" 45 | exec_command "exec_statsinfo -s" 46 | 47 | echo "/**--- Number of snapshots (2) ---**/" 48 | send_query << EOF > /dev/null 49 | INSERT INTO statsrepo.snapshot VALUES (2, 1, '2012-11-01 00:01:00+09', '2nd', '00:00:02', 524288); 50 | EOF 51 | exec_command "exec_statsinfo -l" 52 | exec_command "exec_statsinfo -s" 53 | 54 | echo "/**--- Specify the INSTANCEID that exists in data (list) ---**/" 55 | send_query << EOF > /dev/null 56 | INSERT INTO statsrepo.instance VALUES (2, '5807946214009601531', 'statsinfo', '5433', '8.4.0'); 57 | INSERT INTO statsrepo.snapshot VALUES (3, 2, '2012-11-01 00:03:00+09', '3rd', '00:00:01', 262144); 58 | SELECT setval('statsrepo.instance_instid_seq', 3, false); 59 | SELECT setval('statsrepo.snapshot_snapid_seq', 4, false); 60 | EOF 61 | exec_command "exec_statsinfo -l -i 2" 62 | 63 | echo "/**--- Specify the INSTANCEID that not exists in data (list) ---**/" 64 | exec_command "exec_statsinfo -l -i 3" 65 | 66 | echo "/**--- There are multiple instances data (list) ---**/" 67 | exec_command "exec_statsinfo -s" 68 | 69 | echo "/*---- Get a snapshot ----*/" 70 | echo "/**--- Specify the ASCII character as a comment of the snapshot ---**/" 71 | exec_command "exec_statsinfo2 -S \"COMMENT\"" 72 | sleep ${SNAPSHOT_DELAY} 73 | 74 | echo "/**--- Specify the UTF-8 character as a comment of the snapshot ---**/" 75 | exec_command "exec_statsinfo2 -S \"マルチバイト文字\"" 76 | sleep ${SNAPSHOT_DELAY} 77 | 78 | echo "/**--- Specify the blank character as a comment of the snapshot ---**/" 79 | exec_command "exec_statsinfo2 -S \" \"" 80 | sleep ${SNAPSHOT_DELAY} 81 | 82 | echo "/**--- Specify the empty character as a comment of the snapshot ---**/" 83 | exec_command "exec_statsinfo2 -S \"\"" 84 | sleep ${SNAPSHOT_DELAY} 85 | 86 | echo "/*---- Delete a snapshot ----*/" 87 | echo "/**--- Specify the INSTANCEID that exists in data ---**/" 88 | exec_command "exec_statsinfo -D 3" 89 | sleep ${SNAPSHOT_DELAY} 90 | 91 | echo "/**--- Specify the INSTANCEID that not exists in data ---**/" 92 | exec_command "exec_statsinfo -D 999999" 93 | sleep ${SNAPSHOT_DELAY} 94 | 95 | send_query << EOF 96 | SELECT 97 | snapid, 98 | instid, 99 | '"' || comment || '"' AS comment 100 | FROM 101 | statsrepo.snapshot 102 | ORDER BY 103 | snapid; 104 | EOF 105 | 106 | echo "/*---- pg_statsinfo's agent start / stop ----*/" 107 | echo "/**--- Stop pg_statsinfo's agent ---**/" 108 | exec_command "exec_statsinfo2 --stop" 109 | 110 | echo "/**--- Start pg_statsinfo's agent ---**/" 111 | exec_command "exec_statsinfo2 --start" 112 | 113 | echo "/*---- Quasi-normal pattern ----*/" 114 | echo "/**--- Contain the snapshot that is same acquisition date ---**/" 115 | send_query << EOF > /dev/null 116 | DELETE FROM statsrepo.alert WHERE instid = 3; 117 | DELETE FROM statsrepo.instance WHERE instid = 3; 118 | UPDATE statsrepo.snapshot SET time = '2012-11-01 00:00:00'; 119 | EOF 120 | exec_command "exec_statsinfo -l" 121 | exec_command "exec_statsinfo -s" 122 | -------------------------------------------------------------------------------- /test/script/function-logger.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | . ./script/common.sh 4 | 5 | PGCONFIG=${CONFIG_DIR}/postgresql-logger.conf 6 | 7 | RELOAD_DELAY=3 8 | WRITE_DELAY=1 9 | 10 | trap stop_all_database EXIT 11 | 12 | echo "/*---- Initialize repository DB ----*/" 13 | setup_repository ${REPOSITORY_DATA} ${REPOSITORY_USER} ${REPOSITORY_PORT} ${REPOSITORY_CONFIG} 14 | 15 | echo "/*---- Initialize monitored instance ----*/" 16 | setup_dbcluster ${PGDATA} ${PGUSER} ${PGPORT} ${PGCONFIG} "" "" "" 17 | sleep 3 18 | createuser -SDRl user01 19 | createuser -SDRl user02 20 | psql << EOF 21 | CREATE TABLE tbl01 (id bigint); 22 | CREATE FUNCTION statsinfo.elog(text, text) RETURNS void AS 23 | \$\$ 24 | DECLARE 25 | BEGIN 26 | IF \$1 = 'DEBUG' THEN 27 | RAISE DEBUG '%', \$2; 28 | ELSIF \$1 = 'INFO' THEN 29 | RAISE INFO '%', \$2; 30 | ELSIF \$1 = 'NOTICE' THEN 31 | RAISE NOTICE '%', \$2; 32 | ELSIF \$1 = 'WARNING' THEN 33 | RAISE WARNING '%', \$2; 34 | ELSIF \$1 = 'ERROR' THEN 35 | RAISE EXCEPTION '%', \$2; 36 | ELSIF \$1 = 'LOG' THEN 37 | RAISE LOG '%', \$2; 38 | ELSIF \$1 = 'ALL' THEN 39 | RAISE DEBUG '%', \$2; 40 | RAISE INFO '%', \$2; 41 | RAISE NOTICE '%', \$2; 42 | RAISE WARNING '%', \$2; 43 | RAISE LOG '%', \$2; 44 | RAISE EXCEPTION '%', \$2; 45 | ELSE 46 | RAISE EXCEPTION 'message level % not support', \$1; 47 | END IF; 48 | END; 49 | \$\$ LANGUAGE plpgsql; 50 | EOF 51 | 52 | echo "/*---- Server log filter ----*/" 53 | echo "/**--- Sets the textlog's filename and access permission ---**/" 54 | update_pgconfig ${PGDATA} ".textlog_filename" "'postgresql-statsinfo.log'" 55 | update_pgconfig ${PGDATA} ".textlog_permission" "0644" 56 | pg_ctl restart -w -D ${PGDATA} -o "-p ${PGPORT}" > /dev/null 57 | sleep 3 58 | stat -c "postgresql-statsinfo.log %A(%a)" ${PGLOG_DIR}/postgresql-statsinfo.log 59 | update_pgconfig ${PGDATA} ".textlog_filename" "'pg_statsinfo.log'" 60 | update_pgconfig ${PGDATA} ".textlog_permission" "0666" 61 | pg_ctl reload && sleep ${RELOAD_DELAY} 62 | psql -c "SELECT pg_rotate_logfile()" > /dev/null 63 | sleep ${WRITE_DELAY} 64 | stat -c "pg_statsinfo.log %A(%a)" ${PGLOG_DIR}/pg_statsinfo.log 65 | 66 | echo "/**--- Textlog routing (textlog_min_messages = disable) ---**/" 67 | update_pgconfig ${PGDATA} ".textlog_min_messages" "disable" 68 | pg_ctl reload && sleep ${RELOAD_DELAY} 69 | psql -c "SELECT statsinfo.elog('ALL', 'textlog routing test (disable)')" 2> /dev/null 70 | sleep ${WRITE_DELAY} 71 | grep "textlog routing test (disable)" ${PGLOG_DIR}/pg_statsinfo.log 72 | 73 | echo "/**--- Textlog routing (textlog_min_messages = error) ---**/" 74 | update_pgconfig ${PGDATA} ".textlog_min_messages" "error" 75 | pg_ctl reload && sleep ${RELOAD_DELAY} 76 | psql -c "SELECT statsinfo.elog('ALL', 'textlog routing test (error)')" 2> /dev/null 77 | sleep ${WRITE_DELAY} 78 | grep "textlog routing test (error)" ${PGLOG_DIR}/pg_statsinfo.log 79 | 80 | echo "/**--- Textlog routing (adjust_log_level = off) ---**/" 81 | set_pgconfig ${PGCONFIG} ${PGDATA} 82 | update_pgconfig ${PGDATA} ".adjust_log_level" "off" 83 | update_pgconfig ${PGDATA} ".adjust_log_info" "'42P01'" 84 | pg_ctl reload && sleep ${RELOAD_DELAY} 85 | psql -c "SELECT * FROM xxx" > /dev/null 2>&1 86 | sleep ${WRITE_DELAY} 87 | tail -n 2 ${PGLOG_DIR}/pg_statsinfo.log 88 | 89 | echo "/**--- Adjust log level (adjust_log_info = '42P01') ---**/" 90 | update_pgconfig ${PGDATA} ".adjust_log_level" "on" 91 | update_pgconfig ${PGDATA} ".adjust_log_info" "'42P01'" 92 | pg_ctl reload && sleep ${RELOAD_DELAY} 93 | psql -c "SELECT * FROM xxx" > /dev/null 2>&1 94 | sleep ${WRITE_DELAY} 95 | tail -n 2 ${PGLOG_DIR}/pg_statsinfo.log 96 | 97 | echo "/**--- Adjust log level (adjust_log_notice = '42P01') ---**/" 98 | delete_pgconfig ${PGDATA} ".adjust_log_info" 99 | update_pgconfig ${PGDATA} ".adjust_log_notice" "'42P01'" 100 | pg_ctl reload && sleep ${RELOAD_DELAY} 101 | psql -c "SELECT * FROM xxx" > /dev/null 2>&1 102 | sleep ${WRITE_DELAY} 103 | tail -n 2 ${PGLOG_DIR}/pg_statsinfo.log 104 | 105 | echo "/**--- Adjust log level (adjust_log_warning = '42P01') ---**/" 106 | delete_pgconfig ${PGDATA} ".adjust_log_notice" 107 | update_pgconfig ${PGDATA} ".adjust_log_warning" "'42P01'" 108 | pg_ctl reload && sleep ${RELOAD_DELAY} 109 | psql -c "SELECT * FROM xxx" > /dev/null 2>&1 110 | sleep ${WRITE_DELAY} 111 | tail -n 2 ${PGLOG_DIR}/pg_statsinfo.log 112 | 113 | echo "/**--- Adjust log level (adjust_log_error = '00000') ---**/" 114 | delete_pgconfig ${PGDATA} ".adjust_log_warning" 115 | update_pgconfig ${PGDATA} ".adjust_log_error" "'00000'" 116 | update_pgconfig ${PGDATA} "log_statement" "'all'" 117 | pg_ctl reload && sleep ${RELOAD_DELAY} 118 | psql -c "SELECT 1" > /dev/null 119 | sleep ${WRITE_DELAY} 120 | tail -n 1 ${PGLOG_DIR}/pg_statsinfo.log 121 | 122 | echo "/**--- Adjust log level (adjust_log_log = '42P01') ---**/" 123 | delete_pgconfig ${PGDATA} "log_statement" 124 | delete_pgconfig ${PGDATA} ".adjust_log_error" 125 | update_pgconfig ${PGDATA} ".adjust_log_log" "'42P01'" 126 | pg_ctl reload && sleep ${RELOAD_DELAY} 127 | psql -c "SELECT * FROM xxx" > /dev/null 2>&1 128 | sleep ${WRITE_DELAY} 129 | tail -n 2 ${PGLOG_DIR}/pg_statsinfo.log 130 | 131 | echo "/**--- Adjust log level (adjust_log_fatal = '42P01') ---**/" 132 | delete_pgconfig ${PGDATA} ".adjust_log_log" 133 | update_pgconfig ${PGDATA} ".adjust_log_fatal" "'42P01'" 134 | pg_ctl reload && sleep ${RELOAD_DELAY} 135 | psql -c "SELECT * FROM xxx" > /dev/null 2>&1 136 | sleep ${WRITE_DELAY} 137 | tail -n 2 ${PGLOG_DIR}/pg_statsinfo.log 138 | 139 | echo "/**--- Sets the nologging filter (textlog_nologging_users = 'user01') ---**/" 140 | set_pgconfig ${PGCONFIG} ${PGDATA} 141 | update_pgconfig ${PGDATA} ".textlog_nologging_users" "'user01'" 142 | update_pgconfig ${PGDATA} "log_statement" "'all'" 143 | pg_ctl reload && sleep ${RELOAD_DELAY} 144 | psql -U ${PGUSER} -c "SELECT 1" > /dev/null 145 | psql -U user01 -c "SELECT 2" > /dev/null 146 | sleep ${WRITE_DELAY} 147 | tail -n 1 ${PGLOG_DIR}/pg_statsinfo.log 148 | 149 | echo "/**--- Sets the nologging filter (textlog_nologging_users = 'user01, user02') ---**/" 150 | update_pgconfig ${PGDATA} ".textlog_nologging_users" "'user01, user02'" 151 | pg_ctl reload && sleep ${RELOAD_DELAY} 152 | psql -U ${PGUSER} -c "SELECT 1" > /dev/null 153 | psql -U user01 -c "SELECT 2" > /dev/null 154 | psql -U user02 -c "SELECT 3" > /dev/null 155 | sleep ${WRITE_DELAY} 156 | tail -n 1 ${PGLOG_DIR}/pg_statsinfo.log 157 | 158 | echo "/**--- Collect the CHECKPOINT information ---**/" 159 | set_pgconfig ${PGCONFIG} ${PGDATA} 160 | update_pgconfig ${PGDATA} "log_checkpoints" "on" 161 | update_pgconfig ${PGDATA} "checkpoint_timeout" "30" 162 | if [ $(server_version) -ge 90500 ] ; then 163 | update_pgconfig ${PGDATA} "max_wal_size" "32MB" 164 | else 165 | update_pgconfig ${PGDATA} "checkpoint_segments" "1" 166 | fi 167 | pg_ctl restart -w -D ${PGDATA} -o "-p ${PGPORT}" > /dev/null 168 | sleep 3 169 | psql -c "CHECKPOINT" 170 | psql -c "SELECT ${FUNCTION_PG_SWITCH_WAL}" > /dev/null 171 | sleep 5 172 | psql -c "INSERT INTO tbl01 VALUES (0)" 173 | sleep 30 174 | send_query << EOF 175 | SELECT 176 | instid, 177 | flags, 178 | CASE WHEN start IS NOT NULL THEN 'xxx' END AS start, 179 | CASE WHEN num_buffers IS NOT NULL THEN 'xxx' END AS num_buffers, 180 | CASE WHEN xlog_added IS NOT NULL THEN 'xxx' END AS xlog_added, 181 | CASE WHEN xlog_removed IS NOT NULL THEN 'xxx' END AS xlog_removed, 182 | CASE WHEN xlog_recycled IS NOT NULL THEN 'xxx' END AS xlog_recycled, 183 | CASE WHEN write_duration IS NOT NULL THEN 'xxx' END AS write_duration, 184 | CASE WHEN sync_duration IS NOT NULL THEN 'xxx' END AS sync_duration, 185 | CASE WHEN total_duration IS NOT NULL THEN 'xxx' END AS total_duration 186 | FROM 187 | statsrepo.checkpoint 188 | ORDER BY 189 | flags; 190 | EOF 191 | 192 | echo "/**--- Collect the AUTOANALYZE information ---**/" 193 | set_pgconfig ${PGCONFIG} ${PGDATA} 194 | update_pgconfig ${PGDATA} "autovacuum" "on" 195 | update_pgconfig ${PGDATA} "log_autovacuum_min_duration" "0" 196 | update_pgconfig ${PGDATA} "autovacuum_naptime" "1" 197 | update_pgconfig ${PGDATA} "autovacuum_analyze_threshold" "10000" 198 | update_pgconfig ${PGDATA} "autovacuum_analyze_scale_factor" "0" 199 | update_pgconfig ${PGDATA} "autovacuum_vacuum_threshold" "4000" 200 | update_pgconfig ${PGDATA} "autovacuum_vacuum_scale_factor" "0" 201 | pg_ctl reload && sleep ${RELOAD_DELAY} 202 | psql -c "INSERT INTO tbl01 VALUES (generate_series(1,10000))" 203 | psql -c "DELETE FROM tbl01 WHERE id <= 4000" 204 | sleep 10 205 | send_query << EOF 206 | SELECT 207 | instid, 208 | database, 209 | schema, 210 | "table", 211 | CASE WHEN start IS NOT NULL THEN 'xxx' END AS start, 212 | CASE WHEN duration IS NOT NULL THEN 'xxx' END AS duration 213 | FROM 214 | statsrepo.autoanalyze 215 | ORDER BY 216 | database, schema, "table"; 217 | EOF 218 | 219 | echo "/**--- Collect the AUTOVACUUM information ---**/" 220 | if [ $(server_version) -ge 90400 ] ; then 221 | SELECT_TUP_DEAD="CASE WHEN tup_dead IS NOT NULL THEN 'xxx' END" 222 | else 223 | SELECT_TUP_DEAD="CASE WHEN tup_dead IS NULL THEN '(N/A)' END" 224 | fi 225 | send_query << EOF 226 | SELECT 227 | instid, 228 | database, 229 | schema, 230 | "table", 231 | CASE WHEN start IS NOT NULL THEN 'xxx' END AS start, 232 | CASE WHEN index_scans IS NOT NULL THEN 'xxx' END AS index_scans, 233 | CASE WHEN page_removed IS NOT NULL THEN 'xxx' END AS page_removed, 234 | CASE WHEN page_remain IS NOT NULL THEN 'xxx' END AS page_remain, 235 | CASE WHEN tup_removed IS NOT NULL THEN 'xxx' END AS tup_removed, 236 | CASE WHEN tup_remain IS NOT NULL THEN 'xxx' END AS tup_remain, 237 | ${SELECT_TUP_DEAD} AS tup_dead, 238 | CASE WHEN page_hit IS NOT NULL THEN 'xxx' END AS page_hit, 239 | CASE WHEN page_miss IS NOT NULL THEN 'xxx' END AS page_miss, 240 | CASE WHEN page_dirty IS NOT NULL THEN 'xxx' END AS page_dirty, 241 | CASE WHEN read_rate IS NOT NULL THEN 'xxx' END AS read_rate, 242 | CASE WHEN write_rate IS NOT NULL THEN 'xxx' END AS write_rate, 243 | CASE WHEN duration IS NOT NULL THEN 'xxx' END AS duration 244 | FROM 245 | statsrepo.autovacuum 246 | WHERE 247 | schema = 'public' 248 | ORDER BY 249 | database, schema, "table"; 250 | EOF 251 | 252 | echo "/**--- Collect the cancelled AUTOVACUUM information ---**/" 253 | update_pgconfig ${PGDATA} "log_min_messages" "debug1" 254 | update_pgconfig ${PGDATA} "autovacuum_analyze_threshold" "2147483647" 255 | update_pgconfig ${PGDATA} "autovacuum_vacuum_threshold" "10000" 256 | update_pgconfig ${PGDATA} "autovacuum_vacuum_cost_delay" "100ms" 257 | update_pgconfig ${PGDATA} "autovacuum_vacuum_cost_limit" "1" 258 | pg_ctl reload && sleep ${RELOAD_DELAY} 259 | psql -c "TRUNCATE tbl01" 260 | psql -c "INSERT INTO tbl01 VALUES (generate_series(1,100000))" 261 | psql -c "DELETE FROM tbl01" 262 | sleep 3 263 | psql -c "BEGIN; LOCK TABLE tbl01" 264 | sleep 3 265 | send_query << EOF 266 | SELECT 267 | t1.instid, 268 | CASE WHEN t1.timestamp IS NOT NULL THEN 'xxx' END AS timestamp, 269 | t1.database, 270 | t1.schema, 271 | t1.table, 272 | CASE WHEN 273 | t2.major_version = '901' AND t2.minor_version >= '19' 274 | THEN 275 | '(N/A)' 276 | ELSE 277 | t1.query 278 | END 279 | FROM 280 | statsrepo.autovacuum_cancel t1, 281 | (SELECT substring(current_setting('server_version_num'), 1, 3) AS major_version, 282 | substring(current_setting('server_version_num'), 4, 5) AS minor_version) t2 283 | ORDER BY 284 | t1.database, t1.schema, t1.table; 285 | EOF 286 | -------------------------------------------------------------------------------- /test/script/function-logstore.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | . ./script/common.sh 4 | 5 | PGCONFIG=${CONFIG_DIR}/postgresql-logstore.conf 6 | 7 | RELOAD_DELAY=3 8 | STORE_DELAY=1 9 | 10 | trap stop_all_database EXIT 11 | 12 | echo "/*---- Initialize repository DB ----*/" 13 | setup_repository ${REPOSITORY_DATA} ${REPOSITORY_USER} ${REPOSITORY_PORT} ${REPOSITORY_CONFIG} 14 | 15 | echo "/*---- Initialize monitored instance ----*/" 16 | setup_dbcluster ${PGDATA} ${PGUSER} ${PGPORT} ${PGCONFIG} "" "" "" 17 | sleep 3 18 | createuser -SDRl user01 19 | createuser -SDRl user02 20 | psql << EOF 21 | CREATE TABLE tbl01 (id bigint); 22 | CREATE FUNCTION statsinfo.elog(text, text) RETURNS void AS 23 | \$\$ 24 | DECLARE 25 | BEGIN 26 | IF \$1 = 'DEBUG' THEN 27 | RAISE DEBUG '%', \$2; 28 | ELSIF \$1 = 'INFO' THEN 29 | RAISE INFO '%', \$2; 30 | ELSIF \$1 = 'NOTICE' THEN 31 | RAISE NOTICE '%', \$2; 32 | ELSIF \$1 = 'WARNING' THEN 33 | RAISE WARNING '%', \$2; 34 | ELSIF \$1 = 'ERROR' THEN 35 | RAISE EXCEPTION '%', \$2; 36 | ELSIF \$1 = 'LOG' THEN 37 | RAISE LOG '%', \$2; 38 | ELSIF \$1 = 'ALL' THEN 39 | RAISE DEBUG '%', \$2; 40 | RAISE INFO '%', \$2; 41 | RAISE NOTICE '%', \$2; 42 | RAISE WARNING '%', \$2; 43 | RAISE LOG '%', \$2; 44 | RAISE EXCEPTION '%', \$2; 45 | ELSE 46 | RAISE EXCEPTION 'message level % not support', \$1; 47 | END IF; 48 | END; 49 | \$\$ LANGUAGE plpgsql; 50 | EOF 51 | 52 | echo "/*---- Server Log Accumulation ----*/" 53 | echo "/**--- Log Storing (repolog_min_messages = disable) ---**/" 54 | update_pgconfig ${PGDATA} ".repolog_min_messages" "disable" 55 | pg_ctl reload && sleep ${RELOAD_DELAY} 56 | psql -c "SELECT statsinfo.elog('ALL', 'log storing test (disable)')" 2> /dev/null 57 | sleep ${STORE_DELAY} 58 | send_query -c "SELECT elevel, message FROM statsrepo.log WHERE message = 'log storing test (disable)'" 59 | 60 | echo "/**--- Log Storing (repolog_min_messages = error) ---**/" 61 | update_pgconfig ${PGDATA} ".repolog_min_messages" "error" 62 | pg_ctl reload && sleep ${RELOAD_DELAY} 63 | psql -c "SELECT statsinfo.elog('ALL', 'log storing test (error)')" 2> /dev/null 64 | sleep ${STORE_DELAY} 65 | send_query -c "SELECT elevel, message FROM statsrepo.log WHERE message = 'log storing test (error)'" 66 | 67 | echo "/**--- Sets the nologging filter (repolog_nologging_users = 'user01') ---**/" 68 | set_pgconfig ${PGCONFIG} ${PGDATA} 69 | update_pgconfig ${PGDATA} ".repolog_nologging_users" "'user01'" 70 | update_pgconfig ${PGDATA} "log_statement" "'all'" 71 | pg_ctl reload && sleep ${RELOAD_DELAY} 72 | psql -U user01 -c "SELECT 1" > /dev/null 73 | psql -U user02 -c "SELECT 1" > /dev/null 74 | sleep ${STORE_DELAY} 75 | send_query -c "SELECT elevel, username, message FROM statsrepo.log WHERE username IN ('user01','user02')" 76 | 77 | echo "/**--- Adjust log level (adjust_log_level = off) ---**/" 78 | set_pgconfig ${PGCONFIG} ${PGDATA} 79 | update_pgconfig ${PGDATA} ".adjust_log_level" "off" 80 | update_pgconfig ${PGDATA} ".adjust_log_info" "'42P01'" 81 | pg_ctl reload && sleep ${RELOAD_DELAY} 82 | psql -c "SELECT * FROM xxx" > /dev/null 2>&1 83 | sleep ${STORE_DELAY} 84 | send_query -c "SELECT elevel, sqlstate, message FROM statsrepo.log WHERE sqlstate = '42P01' AND elevel = 'ERROR'" 85 | 86 | echo "/**--- Adjust log level (adjust_log_info = '42P01') ---**/" 87 | update_pgconfig ${PGDATA} ".adjust_log_level" "on" 88 | update_pgconfig ${PGDATA} ".adjust_log_info" "'42P01'" 89 | pg_ctl reload && sleep ${RELOAD_DELAY} 90 | psql -c "SELECT * FROM xxx" > /dev/null 2>&1 91 | sleep ${STORE_DELAY} 92 | send_query -c "SELECT elevel, sqlstate, message FROM statsrepo.log WHERE sqlstate = '42P01' AND elevel = 'INFO'" 93 | 94 | echo "/**--- Adjust log level (adjust_log_notice = '42P01') ---**/" 95 | delete_pgconfig ${PGDATA} ".adjust_log_info" 96 | update_pgconfig ${PGDATA} ".adjust_log_notice" "'42P01'" 97 | pg_ctl reload && sleep ${RELOAD_DELAY} 98 | psql -c "SELECT * FROM xxx" > /dev/null 2>&1 99 | sleep ${STORE_DELAY} 100 | send_query -c "SELECT elevel, sqlstate, message FROM statsrepo.log WHERE sqlstate = '42P01' AND elevel = 'NOTICE'" 101 | 102 | echo "/**--- Adjust log level (adjust_log_warning = '42P01') ---**/" 103 | delete_pgconfig ${PGDATA} ".adjust_log_notice" 104 | update_pgconfig ${PGDATA} ".adjust_log_warning" "'42P01'" 105 | pg_ctl reload && sleep ${RELOAD_DELAY} 106 | psql -c "SELECT * FROM xxx" > /dev/null 2>&1 107 | sleep ${STORE_DELAY} 108 | send_query -c "SELECT elevel, sqlstate, message FROM statsrepo.log WHERE sqlstate = '42P01' AND elevel = 'WARNING'" 109 | 110 | echo "/**--- Adjust log level (adjust_log_error = '00000') ---**/" 111 | delete_pgconfig ${PGDATA} ".adjust_log_warning" 112 | update_pgconfig ${PGDATA} ".adjust_log_error" "'00000'" 113 | update_pgconfig ${PGDATA} "log_statement" "'all'" 114 | pg_ctl reload && sleep ${RELOAD_DELAY} 115 | pid=$(psql -Atc "SELECT pg_backend_pid()") 116 | sleep ${STORE_DELAY} 117 | send_query -c "SELECT elevel, sqlstate, message FROM statsrepo.log WHERE pid = ${pid}" 118 | 119 | echo "/**--- Adjust log level (adjust_log_log = '42P01') ---**/" 120 | delete_pgconfig ${PGDATA} "log_statement" 121 | delete_pgconfig ${PGDATA} ".adjust_log_error" 122 | update_pgconfig ${PGDATA} ".adjust_log_log" "'42P01'" 123 | pg_ctl reload && sleep ${RELOAD_DELAY} 124 | psql -c "SELECT * FROM xxx" > /dev/null 2>&1 125 | sleep ${STORE_DELAY} 126 | send_query -c "SELECT elevel, sqlstate, message FROM statsrepo.log WHERE sqlstate = '42P01' AND elevel = 'LOG'" 127 | 128 | echo "/**--- Adjust log level (adjust_log_fatal = '42P01') ---**/" 129 | delete_pgconfig ${PGDATA} ".adjust_log_log" 130 | update_pgconfig ${PGDATA} ".adjust_log_fatal" "'42P01'" 131 | pg_ctl reload && sleep ${RELOAD_DELAY} 132 | psql -c "SELECT * FROM xxx" > /dev/null 2>&1 133 | sleep ${STORE_DELAY} 134 | send_query -c "SELECT elevel, sqlstate, message FROM statsrepo.log WHERE sqlstate = '42P01' AND elevel = 'FATAL'" 135 | -------------------------------------------------------------------------------- /test/script/function-maintenance.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | . ./script/common.sh 4 | 5 | PGCONFIG=${CONFIG_DIR}/postgresql-maintenance.conf 6 | 7 | RELOAD_DELAY=3 8 | 9 | function get_snapshot() 10 | { 11 | do_snapshot ${PGUSER} ${PGPORT} ${REPOSITORY_USER} ${REPOSITORY_PORT} "${1}" 12 | } 13 | 14 | trap stop_all_database EXIT 15 | 16 | echo "/*---- Initialize repository DB ----*/" 17 | setup_repository ${REPOSITORY_DATA} ${REPOSITORY_USER} ${REPOSITORY_PORT} ${REPOSITORY_CONFIG} 18 | 19 | echo "/*---- Initialize monitored instance ----*/" 20 | setup_dbcluster ${PGDATA} ${PGUSER} ${PGPORT} ${PGCONFIG} "" "" "" 21 | sleep 3 22 | 23 | echo "/*---- Automatic maintenance function ----*/" 24 | echo "/**--- Delete the snapshot for a certain period of time has elapsed ---**/" 25 | get_snapshot "2 days ago" 26 | get_snapshot "1 days ago" 27 | get_snapshot "today" 28 | send_query -c "SELECT snapid, comment FROM statsrepo.snapshot ORDER BY snapid" 29 | send_query << EOF 30 | UPDATE statsrepo.snapshot SET "time" = "time" - '2 day'::interval WHERE snapid = 1; 31 | UPDATE statsrepo.snapshot SET "time" = "time" - '1 day'::interval WHERE snapid = 2; 32 | EOF 33 | maintenance_time=$(psql -Atc "SELECT (now() + '5sec')::time(0)") 34 | update_pgconfig ${PGDATA} ".enable_maintenance" "snapshot" 35 | update_pgconfig ${PGDATA} ".repository_keepday" "1" 36 | update_pgconfig ${PGDATA} ".maintenance_time" "'${maintenance_time}'" 37 | pg_ctl reload && sleep ${RELOAD_DELAY} 38 | sleep 10 39 | send_query -c "SELECT snapid, comment FROM statsrepo.snapshot ORDER BY snapid" 40 | 41 | echo "/**--- Server log maintenance ---**/" 42 | maintenance_time=$(psql -Atc "SELECT (now() + '5sec')::time(0)") 43 | update_pgconfig ${PGDATA} ".enable_maintenance" "log" 44 | update_pgconfig ${PGDATA} ".log_maintenance_command" "'touch %l/ok'" 45 | update_pgconfig ${PGDATA} ".maintenance_time" "'${maintenance_time}'" 46 | pg_ctl reload && sleep ${RELOAD_DELAY} 47 | sleep 10 48 | [ -f ${PGLOG_DIR}/ok ] && 49 | echo "log maintenance command called" 50 | -------------------------------------------------------------------------------- /test/script/function-report.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | . ./script/common.sh 4 | 5 | INPUTDATA_REPOSITORY=${INPUTDATA_DIR}/statsrepo-inputdata.sql 6 | 7 | function exec_statsinfo() 8 | { 9 | pg_statsinfo -U ${REPOSITORY_USER} -p ${REPOSITORY_PORT} "${@}" 10 | } 11 | 12 | trap stop_all_database EXIT 13 | 14 | echo "/*---- Initialize repository DB ----*/" 15 | setup_repository ${REPOSITORY_DATA} ${REPOSITORY_USER} ${REPOSITORY_PORT} ${REPOSITORY_CONFIG} 16 | 17 | export PGOPTIONS=' -c intervalstyle=postgres' 18 | 19 | echo "/*---- Input the repository data ----*/" 20 | send_query -qf "$(pg_config --sharedir)/contrib/pg_statsrepo.sql" 21 | send_query -c "SELECT statsrepo.create_snapshot_partition('2012-11-01')" > /dev/null 22 | send_query -qf ${INPUTDATA_REPOSITORY} 23 | send_query << EOF > /dev/null 24 | SELECT statsrepo.input_data(1, '5807946214009601530', 'statsinfo', 5432, '14.0', 1); 25 | SELECT statsrepo.input_data(2, '5807946214009601531', 'statsinfo', 5433, '14.1', 5); 26 | SELECT statsrepo.input_data(3, '5807946214009601532', 'statsinfo', 5434, '14.2', 9); 27 | SELECT statsrepo.input_data(4, '5807946214009601533', 'statsinfo', 5435, '14.3', 13); 28 | SELECT statsrepo.input_data(5, '5807946214009601534', 'statsinfo', 5436, '14.4', 17); 29 | SELECT statsrepo.input_data(6, '5807946214009601535', 'statsinfo', 5437, '14.5', 21); 30 | EOF 31 | 32 | echo "/*---- Create report ----*/" 33 | echo "/**--- REPORTID: Summary ---**/" 34 | exec_command "exec_statsinfo -r Summary" 35 | 36 | echo "/**--- REPORTID: DatabaseStatistics ---**/" 37 | exec_command "exec_statsinfo -r DatabaseStatistics" 38 | 39 | echo "/**--- REPORTID: InstanceActivity ---**/" 40 | exec_command "exec_statsinfo -r InstanceActivity" 41 | 42 | echo "/**--- REPORTID: OSResourceUsage ---**/" 43 | exec_command "exec_statsinfo -r OSResourceUsage" 44 | 45 | echo "/**--- REPORTID: DiskUsage ---**/" 46 | exec_command "exec_statsinfo -r DiskUsage" 47 | 48 | echo "/**--- REPORTID: LongTransactions ---**/" 49 | exec_command "exec_statsinfo -r LongTransactions" 50 | 51 | echo "/**--- REPORTID: NotableTables ---**/" 52 | exec_command "exec_statsinfo -r NotableTables" 53 | 54 | echo "/**--- REPORTID: CheckpointActivity ---**/" 55 | exec_command "exec_statsinfo -r CheckpointActivity" 56 | 57 | echo "/**--- REPORTID: AutovacuumActivity ---**/" 58 | exec_command "exec_statsinfo -r AutovacuumActivity" 59 | 60 | echo "/**--- REPORTID: QueryActivity ---**/" 61 | exec_command "exec_statsinfo -r QueryActivity" 62 | 63 | echo "/**--- REPORTID: LockConflicts ---**/" 64 | exec_command "exec_statsinfo -r LockConflicts" 65 | 66 | echo "/**--- REPORTID: ReplicationActivity ---**/" 67 | exec_command "exec_statsinfo -r ReplicationActivity" 68 | 69 | echo "/**--- REPORTID: SettingParameters ---**/" 70 | exec_command "exec_statsinfo -r SettingParameters" 71 | 72 | echo "/**--- REPORTID: SchemaInformation ---**/" 73 | exec_command "exec_statsinfo -r SchemaInformation" 74 | 75 | echo "/**--- REPORTID: Alert ---**/" 76 | exec_command "exec_statsinfo -r Alert" 77 | 78 | echo "/**--- REPORTID: Profiles ---**/" 79 | exec_command "exec_statsinfo -r Profiles" 80 | 81 | echo "/**--- REPORTID: HardwareInformation ---**/" 82 | exec_command "exec_statsinfo -r HardwareInformation" 83 | 84 | echo "/**--- REPORTID: All ---**/" 85 | exec_command "exec_statsinfo -r All" 86 | 87 | echo "/**--- Specify the INSTANCEID that exists in data ---**/" 88 | exec_command "exec_statsinfo -r Summary -i 1" 89 | 90 | echo "/**--- Specify the INSTANCEID that not exists in data ---**/" 91 | exec_command "exec_statsinfo -r Summary -i 99" 92 | 93 | echo "/**--- Specify the report scope (-e=2) ---**/" 94 | exec_command "exec_statsinfo -r Summary -i 1 -e 2" 95 | 96 | echo "/**--- Specify the report scope (-b=2, -e=3) ---**/" 97 | exec_command "exec_statsinfo -r Summary -i 1 -b 2 -e 3" 98 | 99 | echo "/**--- Specify the report scope (-b=3) ---**/" 100 | exec_command "exec_statsinfo -r Summary -i 1 -b 3" 101 | 102 | echo "/**--- Specify the report scope (-E=) ---**/" 103 | exec_command "exec_statsinfo -r Summary -i 1 -E '2012-11-01 00:01:00'" 104 | 105 | echo "/**--- Specify the report scope (-B=, -E=) ---**/" 106 | exec_command "exec_statsinfo -r Summary -i 1 -B '2012-11-01 00:01:00' -E '2012-11-01 00:02:00'" 107 | 108 | echo "/**--- Specify the report scope (-B=) ---**/" 109 | exec_command "exec_statsinfo -r Summary -i 1 -B '2012-11-01 00:02:00'" 110 | 111 | echo "/**--- Output the report to a file ---**/" 112 | exec_command "exec_statsinfo -r Summary -i 1 -o ${REPOSITORY_DATA}/report.log" 113 | cat ${REPOSITORY_DATA}/report.log 114 | 115 | echo "/**--- Output the report to a file (overwrite) ---**/" 116 | exec_command "exec_statsinfo -r Summary -i 2 -o ${REPOSITORY_DATA}/report.log" 117 | cat ${REPOSITORY_DATA}/report.log 118 | 119 | echo "/*---- Quasi-normal pattern ----*/" 120 | echo "/**--- Contain the snapshot that is same acquisition date ---**/" 121 | send_query -c "UPDATE statsrepo.snapshot SET time = '2012-11-01 00:00:00' WHERE instid = 6" 122 | exec_command "exec_statsinfo -r All -i 6" 123 | -------------------------------------------------------------------------------- /test/script/function-snapshot_replication.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | . ./script/common.sh 4 | 5 | PGDATA_ACT=${DBCLUSTER_DIR}/pgdata-act 6 | PGCONFIG_ACT=${CONFIG_DIR}/postgresql-replication-act.conf 7 | PGPORT_ACT=57401 8 | HBACONF_REPLICATION=${CONFIG_DIR}/pg_hba-replication.conf 9 | ARCHIVE_DIR=${PGDATA_ACT}/archivelog 10 | 11 | PGDATA_SBY=${DBCLUSTER_DIR}/pgdata-sby 12 | PGCONFIG_SBY=${CONFIG_DIR}/postgresql-replication-sby.conf 13 | PGPORT_SBY=57402 14 | 15 | PGDATA_LOGICAL_SBY=${DBCLUSTER_DIR}/pgdata-logical-sby 16 | PGCONFIG_LOGICAL_SBY=${CONFIG_DIR}/postgresql-logical-sby.conf 17 | PGPORT_LOGICAL_SBY=57403 18 | 19 | function get_snapshot() 20 | { 21 | do_snapshot ${PGUSER} ${1} ${REPOSITORY_USER} ${REPOSITORY_PORT} 22 | } 23 | 24 | trap stop_all_database EXIT 25 | 26 | echo "/*---- Initialize repository DB ----*/" 27 | setup_repository ${REPOSITORY_DATA} ${REPOSITORY_USER} ${REPOSITORY_PORT} ${REPOSITORY_CONFIG} 28 | 29 | echo "/*---- Initialize monitored instance (replication configuration) ----*/" 30 | setup_dbcluster ${PGDATA_ACT} ${PGUSER} ${PGPORT_ACT} ${PGCONFIG_ACT} ${ARCHIVE_DIR} "" ${HBACONF_REPLICATION} 31 | sleep 3 32 | pg_basebackup -h 127.0.0.1 -p ${PGPORT_ACT} -R -D ${PGDATA_SBY} 33 | set_pgconfig ${PGCONFIG_SBY} ${PGDATA_SBY} ${ARCHIVE_DIR} 34 | pg_ctl start -D ${PGDATA_SBY} -o "-p ${PGPORT_SBY}" > /dev/null 35 | 36 | echo "/*---- Initialize logical standby instance ----*/" 37 | setup_dbcluster ${PGDATA_LOGICAL_SBY} ${PGUSER} ${PGPORT_LOGICAL_SBY} ${PGCONFIG_LOGICAL_SBY} "" "" ${HBACONF_REPLICATION} 38 | psql -p ${PGPORT_ACT} -U ${PGUSER} -d postgres << EOF > /dev/null 39 | CREATE TABLE xxx (col int); 40 | CREATE PUBLICATION pub FOR TABLE xxx; 41 | EOF 42 | psql -p ${PGPORT_LOGICAL_SBY} -U ${PGUSER} -d postgres << EOF > /dev/null 43 | CREATE TABLE xxx (col int); 44 | CREATE SUBSCRIPTION sub CONNECTION 'host=127.0.0.1 port=${PGPORT_ACT}' PUBLICATION pub; 45 | EOF 46 | 47 | sleep 1 48 | 49 | echo "/***-- Statistics of WAL (MASTER) --***/" 50 | get_snapshot ${PGPORT_ACT} 51 | send_query << EOF 52 | SELECT 53 | snapid, 54 | CASE WHEN location IS NOT NULL THEN 'xxx' END AS location, 55 | CASE WHEN xlogfile IS NOT NULL THEN 'xxx' END AS xlogfile 56 | FROM 57 | statsrepo.xlog 58 | WHERE 59 | snapid = (SELECT max(snapid) FROM statsrepo.snapshot); 60 | EOF 61 | 62 | echo "/***-- Statistics of archive (MASTER) --***/" 63 | psql -p ${PGPORT_ACT} << EOF > /dev/null 64 | SELECT pg_stat_reset_shared('archiver'); 65 | SELECT ${FUNCTION_PG_SWITCH_WAL}; 66 | SELECT pg_sleep(1); 67 | EOF 68 | get_snapshot ${PGPORT_ACT} 69 | send_query << EOF 70 | SELECT 71 | snapid, 72 | archived_count, 73 | CASE WHEN last_archived_wal IS NOT NULL THEN 'xxx' END AS last_archived_wal, 74 | CASE WHEN last_archived_time IS NOT NULL THEN 'xxx' END AS last_archived_time, 75 | failed_count, 76 | CASE WHEN last_failed_wal IS NOT NULL THEN 'xxx' END AS last_failed_wal, 77 | CASE WHEN last_failed_time IS NOT NULL THEN 'xxx' END AS last_failed_time, 78 | CASE WHEN stats_reset IS NOT NULL THEN 'xxx' END AS stats_reset 79 | FROM 80 | statsrepo.archive 81 | WHERE 82 | snapid = (SELECT max(snapid) FROM statsrepo.snapshot); 83 | EOF 84 | 85 | echo "/***-- Statistics of replication (MASTER) --***/" 86 | send_query << EOF 87 | SELECT 88 | snapid, 89 | CASE WHEN procpid IS NOT NULL THEN 'xxx' END AS procpid, 90 | CASE WHEN usesysid IS NOT NULL THEN 'xxx' END AS usesysid, 91 | usename, 92 | application_name, 93 | CASE WHEN client_addr IS NOT NULL THEN 'xxx' END AS client_addr, 94 | CASE WHEN client_hostname IS NOT NULL THEN 'xxx' END AS client_hostname, 95 | CASE WHEN client_port IS NOT NULL THEN 'xxx' END AS client_port, 96 | CASE WHEN backend_start IS NOT NULL THEN 'xxx' END AS backend_start, 97 | CASE WHEN backend_xmin IS NOT NULL THEN 'xxx' END AS backend_xmin, 98 | state, 99 | CASE WHEN current_location IS NOT NULL THEN 'xxx' END AS current_location, 100 | CASE WHEN sent_location IS NOT NULL THEN 'xxx' END AS sent_location, 101 | CASE WHEN write_location IS NOT NULL THEN 'xxx' END AS write_location, 102 | CASE WHEN flush_location IS NOT NULL THEN 'xxx' END AS flush_location, 103 | CASE WHEN replay_location IS NOT NULL THEN 'xxx' END AS replay_location, 104 | CASE WHEN write_lag IS NOT NULL THEN 'xxx' END AS write_lag, 105 | CASE WHEN flush_lag IS NOT NULL THEN 'xxx' END AS flush_lag, 106 | CASE WHEN replay_lag IS NOT NULL THEN 'xxx' END AS replay_lag, 107 | sync_priority, 108 | sync_state 109 | FROM 110 | statsrepo.replication 111 | WHERE 112 | snapid = (SELECT max(snapid) FROM statsrepo.snapshot) 113 | ORDER BY 114 | application_name; 115 | EOF 116 | 117 | echo "/***-- Statistics of replication slot (MASTER) --***/" 118 | send_query << EOF 119 | SELECT 120 | snapid, 121 | slot_name, 122 | plugin, 123 | slot_type, 124 | CASE WHEN datoid IS NOT NULL THEN 'xxx' END AS datoid, 125 | temporary, 126 | active, 127 | CASE WHEN active_pid IS NOT NULL THEN 'xxx' END AS active_pid, 128 | CASE WHEN xact_xmin IS NOT NULL THEN 'xxx' END AS xact_xmin, 129 | CASE WHEN catalog_xmin IS NOT NULL THEN 'xxx' END AS catalog_xmin, 130 | CASE WHEN restart_lsn IS NOT NULL THEN 'xxx' END AS restart_lsn, 131 | CASE WHEN confirmed_flush_lsn IS NOT NULL THEN 'xxx' END AS confirmed_flush_lsn 132 | FROM 133 | statsrepo.replication_slots 134 | WHERE 135 | snapid = (SELECT max(snapid) FROM statsrepo.snapshot) 136 | EOF 137 | 138 | echo "/***-- Statistics of WAL (STANDBY) --***/" 139 | get_snapshot ${PGPORT_SBY} 140 | send_query -c "SELECT * FROM statsrepo.xlog WHERE snapid = (SELECT max(snapid) FROM statsrepo.snapshot)" 141 | 142 | echo "/***-- Statistics of archive (STANDBY) --***/" 143 | send_query << EOF 144 | SELECT 145 | snapid, 146 | archived_count, 147 | last_archived_wal, 148 | last_archived_time, 149 | failed_count, 150 | last_failed_wal, 151 | last_failed_time, 152 | CASE WHEN stats_reset IS NOT NULL THEN 'xxx' END AS stats_reset 153 | FROM 154 | statsrepo.archive 155 | WHERE 156 | snapid = (SELECT max(snapid) FROM statsrepo.snapshot); 157 | EOF 158 | 159 | echo "/***-- Statistics of replication (STANDBY) --***/" 160 | send_query -c "SELECT * FROM statsrepo.replication WHERE snapid = (SELECT max(snapid) FROM statsrepo.snapshot)" 161 | --------------------------------------------------------------------------------