Issue(30): Code refactoring.

pull/45/head
Ibrar Ahmed 2020-09-14 22:26:19 +00:00
parent 7613b1cb1c
commit 935d063f13
6 changed files with 554 additions and 713 deletions

View File

@ -1,7 +1,7 @@
# contrib/pg_stat_monitor/Makefile
MODULE_big = pg_stat_monitor
OBJS = guc.o pg_stat_monitor.o $(WIN32RES)
OBJS = hash_query.o guc.o pg_stat_monitor.o $(WIN32RES)
EXTENSION = pg_stat_monitor
DATA = pg_stat_monitor--1.0.sql

12
guc.c
View File

@ -81,9 +81,9 @@ init_guc(void)
conf[i++] = (GucVariable) {
.guc_name = "pg_stat_monitor.pgsm_object_cache",
.guc_desc = "Sets the maximum number of object cache",
.guc_default = 5,
.guc_min = 5,
.guc_max = 10,
.guc_default = 50,
.guc_min = 50,
.guc_max = INT_MAX,
.guc_restart = true
};
@ -214,9 +214,9 @@ init_guc(void)
"Sets the maximum number of object cache",
NULL,
&PGSM_OBJECT_CACHE,
5,
5,
10,
50,
50,
INT_MAX,
PGC_POSTMASTER,
0,
NULL,

383
hash_query.c Normal file
View File

@ -0,0 +1,383 @@
/*-------------------------------------------------------------------------
*
* hash_query.c
* Track statement execution times across a whole database cluster.
*
* Copyright (c) 2008-2018, PostgreSQL Global Development Group
*
* IDENTIFICATION
* contrib/pg_stat_monitor/hash_query.c
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "pg_stat_monitor.h"
static pgssSharedState *pgss;
static HTAB *pgss_hash;
static HTAB *pgss_object_hash;
static HTAB *pgss_buckethash = NULL;
static HTAB *pgss_waiteventshash = NULL;
static pgssBucketEntry **pgssBucketEntries = NULL;
static pgssWaitEventEntry **pgssWaitEventEntries = NULL;
static HTAB* hash_init(const char *hash_name, int key_size, int entry_size, int hash_size);
static HTAB*
hash_init(const char *hash_name, int key_size, int entry_size, int hash_size)
{
HASHCTL info;
memset(&info, 0, sizeof(info));
info.keysize = key_size;
info.entrysize = entry_size;
return ShmemInitHash(hash_name, hash_size, hash_size, &info, HASH_ELEM | HASH_BLOBS);
}
/*
* shmem_startup hook: allocate or attach to shared memory,
* then load any pre-existing statistics from file.
* Also create and load the query-texts file, which is expected to exist
* (even if empty) while the module is enabled.
*/
void
pgss_shmem_startup(void)
{
bool found = false;
int32 i;
if (prev_shmem_startup_hook)
prev_shmem_startup_hook();
/* reset in case this is a restart within the postmaster */
pgss = NULL;
pgss_hash = NULL;
pgss_object_hash = NULL;
pgss_buckethash = NULL;
pgss_waiteventshash = NULL;
/*
* Create or attach to the shared memory state, including hash table
*/
LWLockAcquire(AddinShmemInitLock, LW_EXCLUSIVE);
pgss = ShmemInitStruct("pg_stat_monitor", sizeof(pgssSharedState), &found);
if (!found)
{
/* First time through ... */
pgss->lock = &(GetNamedLWLockTranche("pg_stat_monitor"))->lock;
SpinLockInit(&pgss->mutex);
ResetSharedState(pgss);
}
pgss->query_buf_size_bucket = PGSM_QUERY_BUF_SIZE / PGSM_MAX_BUCKETS;
for (i = 0; i < PGSM_MAX_BUCKETS; i++)
pgss_qbuf[i] = (unsigned char *) ShmemAlloc(pgss->query_buf_size_bucket);
pgss_hash = hash_init("pg_stat_monitor: Queries hashtable",
sizeof(pgssHashKey),
sizeof(pgssEntry),
PGSM_MAX);
pgss_buckethash = hash_init("pg_stat_monitor: Bucket hashtable",
sizeof(pgssBucketHashKey),
sizeof(pgssBucketEntry),
PGSM_MAX_BUCKETS);
pgss_waiteventshash = hash_init("pg_stat_monitor: Wait Event hashtable",
sizeof(pgssWaitEventKey),
sizeof(pgssWaitEventEntry),
100);
pgss_object_hash = hash_init("pg_stat_monitor: Object hashtable",
sizeof(pgssObjectHashKey),
sizeof(pgssObjectEntry),
PGSM_OBJECT_CACHE);
Assert(IsHashInitialize());
pgssWaitEventEntries = malloc(sizeof (pgssWaitEventEntry) * MAX_BACKEND_PROCESES);
for (i = 0; i < MAX_BACKEND_PROCESES; i++)
{
pgssWaitEventKey key;
pgssWaitEventEntry *entry = NULL;
bool found = false;
key.processid = i;
entry = (pgssWaitEventEntry *) hash_search(pgss_waiteventshash, &key, HASH_ENTER, &found);
if (!found)
{
SpinLockInit(&entry->mutex);
pgssWaitEventEntries[i] = entry;
}
}
pgssBucketEntries = malloc(sizeof (pgssBucketEntry) * PGSM_MAX_BUCKETS);
for (i = 0; i < PGSM_MAX_BUCKETS; i++)
{
pgssBucketHashKey key;
pgssBucketEntry *entry = NULL;
bool found = false;
key.bucket_id = i;
/* Find or create an entry with desired hash code */
entry = (pgssBucketEntry *) hash_search(pgss_buckethash, &key, HASH_ENTER, &found);
if (!found)
{
memset(&entry->counters, 0, sizeof(pgssBucketCounters));
SpinLockInit(&entry->mutex);
pgssBucketEntries[i] = entry;
}
}
LWLockRelease(AddinShmemInitLock);
/*
* If we're in the postmaster (or a standalone backend...), set up a shmem
* exit hook to dump the statistics to disk.
*/
if (!IsUnderPostmaster)
on_shmem_exit(pgss_shmem_shutdown, (Datum) 0);
}
int
pgsm_get_bucket_size(void)
{
Assert(pgss->query_buf_size_bucket <= 0);
return pgss->query_buf_size_bucket;
}
pgssSharedState* pgsm_get_ss(void)
{
Assert(pgss);
return pgss;
}
HTAB* pgsm_get_hash(void)
{
return pgss_hash;
}
pgssBucketEntry** pgsm_get_bucket_entries(void)
{
return pgssBucketEntries;
}
HTAB* pgsm_get_wait_event_hash(void)
{
return pgss_waiteventshash;
}
pgssBucketEntry** pgsm_get_bucket(void)
{
return pgssBucketEntries;
}
pgssWaitEventEntry** pgsm_get_wait_event_entry(void)
{
return pgssWaitEventEntries;
}
/*
* shmem_shutdown hook: Dump statistics into file.
*
* Note: we don't bother with acquiring lock, because there should be no
* other processes running when this is called.
*/
void
pgss_shmem_shutdown(int code, Datum arg)
{
elog(DEBUG2, "pg_stat_monitor: %s()", __FUNCTION__);
/* Don't try to dump during a crash. */
if (code)
return;
/* Safety check ... shouldn't get here unless shmem is set up. */
if (IsHashInitialize())
return;
}
Size
hash_memsize(void)
{
Size size;
size = MAXALIGN(sizeof(pgssSharedState));
size = add_size(size, hash_estimate_size(PGSM_MAX, sizeof(pgssEntry)));
return size;
}
pgssEntry *
hash_entry_alloc(pgssSharedState *pgss, pgssHashKey *key,int encoding)
{
pgssEntry *entry = NULL;
bool found = false;
if (pgss->bucket_entry[pgss->current_wbucket] >= (PGSM_MAX / PGSM_MAX_BUCKETS))
{
pgss->bucket_overflow[pgss->current_wbucket]++;
return NULL;
}
if (hash_get_num_entries(pgss_hash) >= PGSM_MAX)
return NULL;
/* Find or create an entry with desired hash code */
entry = (pgssEntry *) hash_search(pgss_hash, key, HASH_ENTER, &found);
if (!found)
{
pgss->bucket_entry[pgss->current_wbucket]++;
/* New entry, initialize it */
/* reset the statistics */
memset(&entry->counters, 0, sizeof(Counters));
/* set the appropriate initial usage count */
/* re-initialize the mutex each time ... we assume no one using it */
SpinLockInit(&entry->mutex);
/* ... and don't forget the query text metadata */
entry->encoding = encoding;
}
return entry;
}
/*
* Deallocate least-used entries.
*
* Caller must hold an exclusive lock on pgss->lock.
*/
void
hash_entry_dealloc(int bucket)
{
HASH_SEQ_STATUS hash_seq;
pgssEntry *entry;
pgssEntry **entries;
int i;
int nvictims = 0;
pgss->bucket_entry[bucket] = 0;
entries = palloc(hash_get_num_entries(pgss_hash) * sizeof(pgssEntry *));
hash_seq_init(&hash_seq, pgss_hash);
while ((entry = hash_seq_search(&hash_seq)) != NULL)
{
if (entry->key.bucket_id == bucket || bucket < 0)
entries[nvictims++] = entry;
}
for (i = 0; i < nvictims; i++)
entry = hash_search(pgss_hash, &entries[i]->key, HASH_REMOVE, NULL);
pfree(entries);
}
/*
* Release all entries.
*/
void
hash_entry_reset()
{
HASH_SEQ_STATUS hash_seq;
pgssEntry *entry;
pgssObjectEntry *objentry;
pgssWaitEventEntry *weentry;
LWLockAcquire(pgss->lock, LW_EXCLUSIVE);
hash_seq_init(&hash_seq, pgss_hash);
while ((entry = hash_seq_search(&hash_seq)) != NULL)
{
hash_search(pgss_hash, &entry->key, HASH_REMOVE, NULL);
}
hash_seq_init(&hash_seq, pgss_buckethash);
while ((objentry = hash_seq_search(&hash_seq)) != NULL)
{
hash_search(pgss_buckethash, &objentry->key, HASH_REMOVE, NULL);
}
hash_seq_init(&hash_seq, pgss_waiteventshash);
while ((weentry = hash_seq_search(&hash_seq)) != NULL)
{
hash_search(pgss_waiteventshash, &weentry->key, HASH_REMOVE, NULL);
}
pgss->current_wbucket = 0;
free(pgssWaitEventEntries);
free(pgssBucketEntries);
LWLockRelease(pgss->lock);
}
void
add_object_entry(uint64 queryid, char *objects)
{
pgssObjectEntry *entry = NULL;
bool found;
pgssObjectHashKey key;
LWLockAcquire(pgss->lock, LW_EXCLUSIVE);
key.queryid = queryid;
entry = (pgssObjectEntry *) hash_search(pgss_object_hash, &key, HASH_ENTER, &found);
if (!found)
{
SpinLockAcquire(&entry->mutex);
snprintf(entry->tables_name, MAX_REL_LEN, "%s", objects);
SpinLockRelease(&entry->mutex);
}
LWLockRelease(pgss->lock);
}
/* De-alocate memory */
void
remove_object_entry(uint64 queryid, char *objects)
{
pgssObjectHashKey key;
pgssObjectEntry *entry;
key.queryid = queryid;
LWLockAcquire(pgss->lock, LW_SHARED);
entry = (pgssObjectEntry *) hash_search(pgss_object_hash, &key, HASH_FIND, NULL);
if (entry != NULL)
{
snprintf(objects, MAX_REL_LEN, "%s", entry->tables_name);
hash_search(pgss_object_hash, &entry->key, HASH_REMOVE, NULL);
}
LWLockRelease(pgss->lock);
}
pgssEntry*
pgsm_create_query_entry(unsigned int queryid,
unsigned int userid,
unsigned int dbid,
unsigned int bucket_id,
unsigned int ip)
{
pgssHashKey key;
pgssEntry *entry = NULL;
int encoding = GetDatabaseEncoding();
key.queryid = queryid;
key.userid = userid;
key.dbid = dbid;
key.bucket_id = bucket_id;
key.ip = ip;
/* Lookup the hash table entry with shared lock. */
LWLockAcquire(pgss->lock, LW_SHARED);
entry = (pgssEntry *) hash_search(pgss_hash, &key, HASH_FIND, NULL);
if(!entry)
{
LWLockRelease(pgss->lock);
LWLockAcquire(pgss->lock, LW_EXCLUSIVE);
/* OK to create a new hashtable entry */
entry = hash_entry_alloc(pgss, &key, encoding);
}
return entry;
}
bool IsHashInitialize(void)
{
return (pgss || pgss_hash || pgss_object_hash || pgss_buckethash || pgss_waiteventshash);
}

View File

@ -10,48 +10,48 @@ AS 'MODULE_PATHNAME'
LANGUAGE C PARALLEL SAFE;
CREATE FUNCTION pg_stat_monitor(IN showtext boolean,
OUT bucket int,
OUT userid oid,
OUT dbid oid,
OUT queryid text,
OUT query text,
OUT bucket_start_time timestamptz,
OUT bucket int,
OUT userid oid,
OUT dbid oid,
OUT client_ip bigint,
OUT queryid text,
OUT query text,
OUT bucket_start_time timestamptz,
OUT plan_calls int8,
OUT plan_total_time float8,
OUT plan_min_time float8,
OUT plan_max_time float8,
OUT plan_mean_time float8,
OUT plan_stddev_time float8,
OUT plan_rows int8,
OUT plan_calls int8,
OUT plan_total_time float8,
OUT plan_min_time float8,
OUT plan_max_time float8,
OUT plan_mean_time float8,
OUT plan_stddev_time float8,
OUT plan_rows int8,
OUT calls int8,
OUT total_time float8,
OUT min_time float8,
OUT max_time float8,
OUT mean_time float8,
OUT stddev_time float8,
OUT rows int8,
OUT total_calls int8,
OUT total_time float8,
OUT min_time float8,
OUT max_time float8,
OUT mean_time float8,
OUT stddev_time float8,
OUT effected_rows int8,
OUT shared_blks_hit int8,
OUT shared_blks_read int8,
OUT shared_blks_hit int8,
OUT shared_blks_read int8,
OUT shared_blks_dirtied int8,
OUT shared_blks_written int8,
OUT local_blks_hit int8,
OUT local_blks_read int8,
OUT local_blks_dirtied int8,
OUT local_blks_written int8,
OUT temp_blks_read int8,
OUT temp_blks_written int8,
OUT blk_read_time float8,
OUT blk_write_time float8,
OUT client_ip bigint,
OUT resp_calls text,
OUT cpu_user_time float8,
OUT cpu_sys_time float8,
OUT tables_names text
OUT local_blks_hit int8,
OUT local_blks_read int8,
OUT local_blks_dirtied int8,
OUT local_blks_written int8,
OUT temp_blks_read int8,
OUT temp_blks_written int8,
OUT blk_read_time float8,
OUT blk_write_time float8,
OUT resp_calls text,
OUT cpu_user_time float8,
OUT cpu_sys_time float8,
OUT tables_names text
)
RETURNS SETOF record
AS 'MODULE_PATHNAME', 'pg_stat_monitor'
@ -90,37 +90,29 @@ CREATE VIEW pg_stat_monitor_settings AS SELECT
restart
FROM pg_stat_monitor_settings();
CREATE FUNCTION pg_stat_agg(
OUT queryid text,
OUT id bigint,
OUT type bigint,
OUT total_calls int)
RETURNS SETOF record
AS 'MODULE_PATHNAME', 'pg_stat_agg'
LANGUAGE C STRICT VOLATILE PARALLEL SAFE;
-- Register a view on the function for ease of use.
CREATE VIEW pg_stat_monitor AS SELECT
bucket,
bucket_start_time,
userid,
dbid,
m.queryid,
'0.0.0.0'::inet + client_ip AS client_ip,
queryid,
query,
plan_calls,
round( CAST(plan_total_time as numeric), 2) as plan_total_time,
round( CAST(plan_min_time as numeric), 2) as plan_min_timei,
round( CAST(plan_max_time as numeric), 2) as plan_max_time,
round( CAST(plan_mean_time as numeric), 2) as plan_mean_time,
round( CAST(plan_stddev_time as numeric), 2) as plan_stddev_time,
round( CAST(plan_total_time as numeric), 2)::float8 as plan_total_time,
round( CAST(plan_min_time as numeric), 2)::float8 as plan_min_timei,
round( CAST(plan_max_time as numeric), 2)::float8 as plan_max_time,
round( CAST(plan_mean_time as numeric), 2)::float8 as plan_mean_time,
round( CAST(plan_stddev_time as numeric), 2)::float8 as plan_stddev_time,
plan_rows,
calls,
round( CAST(total_time as numeric), 2)as total_time,
round( CAST(min_time as numeric), 2)as min_time,
round( CAST(max_time as numeric), 2)as max_time,
round( CAST(mean_time as numeric), 2)as mean_time,
round( CAST(stddev_time as numeric), 2)as stddev_time,
rows,
total_calls,
round( CAST(total_time as numeric), 2)::float8 as total_time,
round( CAST(min_time as numeric), 2)::float8 as min_time,
round( CAST(max_time as numeric), 2)::float8 as max_time,
round( CAST(mean_time as numeric), 2)::float8 as mean_time,
round( CAST(stddev_time as numeric), 2)::float8 as stddev_time,
effected_rows,
shared_blks_hit,
shared_blks_read,
shared_blks_dirtied,
@ -133,15 +125,11 @@ CREATE VIEW pg_stat_monitor AS SELECT
temp_blks_written,
blk_read_time,
blk_write_time,
client_ip as host,
'0.0.0.0'::inet + client_ip AS client_ip,
(string_to_array(resp_calls, ',')) resp_calls,
cpu_user_time,
cpu_sys_time,
(string_to_array(tables_names, ',')) tables_names,
wait_event,
wait_event_type
from pg_stat_monitor(true) m LEFT OUTER JOIN pg_stat_wait_events() w ON w.queryid = m.queryid;
(string_to_array(tables_names, ',')) tables_names
FROM pg_stat_monitor(TRUE);
-- Register a view on the function for ease of use.
@ -152,77 +140,25 @@ CREATE VIEW pg_stat_wait_events AS SELECT
wait_event_type
FROM pg_stat_monitor(true) m, pg_stat_wait_events() w WHERE w.queryid = m.queryid;
GRANT SELECT ON pg_stat_wait_events TO PUBLIC;
GRANT SELECT ON pg_stat_monitor TO PUBLIC;
CREATE VIEW pg_stat_agg_database AS
/*CREATE VIEW pg_stat_monitor_db AS
SELECT
ss.bucket,
agg.queryid,
agg.id AS dbid,
ss.userid,
client_ip,
agg.total_calls,
ss.min_time,
ss.max_time,
ss.mean_time,
ss.resp_calls,
ss.cpu_user_time,
ss.cpu_sys_time,
ss.query,
ss.tables_names
FROM pg_stat_agg() agg
INNER JOIN (SELECT DISTINCT bucket, queryid, dbid, userid, query, client_ip, min_time, max_time, mean_time, resp_calls, tables_names, cpu_user_time,cpu_sys_time
FROM pg_stat_monitor) ss
ON agg.queryid = ss.queryid AND agg.type = 0 AND id = dbid;
*
FROM pg_stat_monitor GROUP BY dbid;
CREATE VIEW pg_stat_agg_user AS
CREATE VIEW pg_stat_monitor_user AS
SELECT
ss.bucket,
agg.queryid,
agg.id AS dbid,
ss.userid,
client_ip,
agg.total_calls,
ss.min_time,
ss.max_time,
ss.mean_time,
ss.resp_calls,
ss.cpu_user_time,
ss.cpu_sys_time,
ss.query,
ss.tables_names
FROM pg_stat_agg() agg
INNER JOIN (SELECT DISTINCT bucket, queryid, userid, query, client_ip, min_time, max_time, mean_time, resp_calls, tables_names, cpu_user_time,cpu_sys_time FROM pg_stat_monitor) ss
ON agg.queryid = ss.queryid AND agg.type = 1 AND id = userid;
*
FROM pg_stat_monitor GROUP BY userid;
CREATE VIEW pg_stat_agg_ip AS
CREATE VIEW pg_stat_monitor_ip AS
SELECT
ss.bucket,
agg.queryid,
agg.id AS dbid,
ss.userid,
ss.client_ip,
ss.host,
agg.total_calls,
ss.min_time,
ss.max_time,
ss.mean_time,
ss.resp_calls,
ss.cpu_user_time,
ss.cpu_sys_time,
ss.query,
ss.tables_names
FROM pg_stat_agg() agg
INNER JOIN (SELECT DISTINCT bucket, queryid, userid, query, client_ip, host, min_time, max_time, mean_time, resp_calls, tables_names, cpu_user_time,cpu_sys_time FROM pg_stat_monitor) ss
ON agg.queryid = ss.queryid AND agg.type = 2 AND id = host;
*
FROM pg_stat_monitor GROUP BY client_ip;
GRANT SELECT ON pg_stat_agg_user TO PUBLIC;
GRANT SELECT ON pg_stat_agg_ip TO PUBLIC;
GRANT SELECT ON pg_stat_agg_database TO PUBLIC;
GRANT SELECT ON pg_stat_monitor_settings TO PUBLIC;
*/
-- Don't want this to be available to non-superusers.
REVOKE ALL ON FUNCTION pg_stat_monitor_reset() FROM PUBLIC;

View File

@ -20,27 +20,23 @@ PG_MODULE_MAGIC;
void _PG_init(void);
void _PG_fini(void);
/*---- Local variables ----*/
/* Current nesting depth of ExecutorRun+ProcessUtility calls */
static int nested_level = 0;
#if PG_VERSION_NUM >= 130000
static int plan_nested_level = 0;
static int exec_nested_level = 0;
#endif
static struct rusage rusage_start;
static struct rusage rusage_end;
static volatile sig_atomic_t sigterm = false;
static void handle_sigterm(SIGNAL_ARGS);
int query_buf_size_bucket;
HTAB *
CreateHash(const char *hash_name, int key_size, int entry_size, int hash_size);
/* Saved hook values in case of unload */
static planner_hook_type planner_hook_next = NULL;
static shmem_startup_hook_type prev_shmem_startup_hook = NULL;
static post_parse_analyze_hook_type prev_post_parse_analyze_hook = NULL;
static ExecutorStart_hook_type prev_ExecutorStart = NULL;
static ExecutorRun_hook_type prev_ExecutorRun = NULL;
@ -48,23 +44,6 @@ static ExecutorFinish_hook_type prev_ExecutorFinish = NULL;
static ExecutorEnd_hook_type prev_ExecutorEnd = NULL;
static ProcessUtility_hook_type prev_ProcessUtility = NULL;
/* Links to shared memory state */
static pgssSharedState *pgss = NULL;
static HTAB *pgss_hash = NULL;
static HTAB *pgss_object_hash = NULL;
/* Hash table for aggegates */
static HTAB *pgss_agghash = NULL;
/* Hash table for aggegates */
static HTAB *pgss_buckethash = NULL;
/* Hash table for wait events */
static HTAB *pgss_waiteventshash = NULL;
static pgssBucketEntry **pgssBucketEntries = NULL;
static pgssWaitEventEntry **pgssWaitEventEntries = NULL;
PG_FUNCTION_INFO_V1(pg_stat_monitor_reset);
PG_FUNCTION_INFO_V1(pg_stat_monitor_1_2);
@ -73,14 +52,9 @@ PG_FUNCTION_INFO_V1(pg_stat_monitor);
PG_FUNCTION_INFO_V1(pg_stat_wait_events);
PG_FUNCTION_INFO_V1(pg_stat_monitor_settings);
/* Extended version function prototypes */
PG_FUNCTION_INFO_V1(pg_stat_agg);
static uint pg_get_client_addr(void);
static Datum array_get_datum(int arr[]);
static void update_agg_counters(uint64 bucket_id, uint64 queryid, uint64 id, AGG_KEY type);
static pgssAggEntry *agg_entry_alloc(pgssAggHashKey *key);
void add_object_entry(uint64 queryid, char *objects);
#if PG_VERSION_NUM >= 130000
static PlannedStmt * pgss_planner_hook(Query *parse, const char *query_string, int cursorOptions, ParamListInfo boundParams);
#else
@ -88,8 +62,6 @@ static void BufferUsageAccumDiff(BufferUsage* bufusage, BufferUsage* pgBufferUsa
static PlannedStmt *pgss_planner_hook(Query *parse, int opt, ParamListInfo param);
#endif
static void pgss_shmem_startup(void);
static void pgss_shmem_shutdown(int code, Datum arg);
static void pgss_post_parse_analyze(ParseState *pstate, Query *query);
static void pgss_ExecutorStart(QueryDesc *queryDesc, int eflags);
static void pgss_ExecutorRun(QueryDesc *queryDesc, ScanDirection direction, uint64 count, bool execute_once);
@ -125,11 +97,7 @@ static void pgss_store(const char *query, uint64 queryId,
static void pg_stat_monitor_internal(FunctionCallInfo fcinfo,
bool showtext);
static Size pgss_memsize(void);
static pgssEntry *entry_alloc(pgssSharedState *pgss, pgssHashKey *key, Size query_offset, int query_len, int encoding, bool sticky);
static void entry_dealloc(int bucket_id);
static void entry_reset(void);
static void AppendJumble(pgssJumbleState *jstate,
const unsigned char *item, Size size);
static void JumbleQuery(pgssJumbleState *jstate, Query *query);
@ -181,7 +149,7 @@ _PG_init(void)
* the postmaster process.) We'll allocate or attach to the shared
* resources in pgss_shmem_startup().
*/
RequestAddinShmemSpace(pgss_memsize());
RequestAddinShmemSpace(hash_memsize());
RequestNamedLWLockTranche("pg_stat_monitor", 1);
/* Register Wait events */
@ -223,152 +191,9 @@ _PG_fini(void)
ExecutorFinish_hook = prev_ExecutorFinish;
ExecutorEnd_hook = prev_ExecutorEnd;
ProcessUtility_hook = prev_ProcessUtility;
entry_reset();
hash_entry_reset();
}
HTAB *
CreateHash(const char *hash_name, int key_size, int entry_size, int hash_size)
{
HASHCTL info;
memset(&info, 0, sizeof(info));
info.keysize = key_size;
info.entrysize = entry_size;
return ShmemInitHash(hash_name, hash_size, hash_size, &info, HASH_ELEM | HASH_BLOBS);
}
/*
* shmem_startup hook: allocate or attach to shared memory,
* then load any pre-existing statistics from file.
* Also create and load the query-texts file, which is expected to exist
* (even if empty) while the module is enabled.
*/
static void
pgss_shmem_startup(void)
{
bool found = false;
int32 i;
if (prev_shmem_startup_hook)
prev_shmem_startup_hook();
/* reset in case this is a restart within the postmaster */
pgss = NULL;
pgss_hash = NULL;
pgss_object_hash = NULL;
pgss_agghash = NULL;
pgss_buckethash = NULL;
pgss_waiteventshash = NULL;
/*
* Create or attach to the shared memory state, including hash table
*/
LWLockAcquire(AddinShmemInitLock, LW_EXCLUSIVE);
pgss = ShmemInitStruct("pg_stat_monitor", sizeof(pgssSharedState), &found);
if (!found)
{
/* First time through ... */
pgss->lock = &(GetNamedLWLockTranche("pg_stat_monitor"))->lock;
SpinLockInit(&pgss->mutex);
ResetSharedState(pgss);
}
query_buf_size_bucket = PGSM_QUERY_BUF_SIZE / PGSM_MAX_BUCKETS;
for (i = 0; i < PGSM_MAX_BUCKETS; i++)
pgss_qbuf[i] = (unsigned char *) ShmemAlloc(query_buf_size_bucket);
pgss_hash = CreateHash("pg_stat_monitor: Queries hashtable",
sizeof(pgssHashKey),
sizeof(pgssEntry),
PGSM_MAX);
pgss_buckethash = CreateHash("pg_stat_monitor: Bucket hashtable",
sizeof(pgssBucketHashKey),
sizeof(pgssBucketEntry),
PGSM_MAX_BUCKETS);
pgss_waiteventshash = CreateHash("pg_stat_monitor: Wait Event hashtable",
sizeof(pgssWaitEventKey),
sizeof(pgssWaitEventEntry),
100);
pgss_object_hash = CreateHash("pg_stat_monitor: Object hashtable",
sizeof(pgssObjectHashKey),
sizeof(pgssObjectEntry),
PGSM_OBJECT_CACHE);
pgss_agghash = CreateHash("pg_stat_monitor: Aggregate hashtable",
sizeof(pgssAggHashKey),
sizeof(pgssAggEntry),
PGSM_MAX * 3);
Assert(IsHashInitialize());
pgssWaitEventEntries = malloc(sizeof (pgssWaitEventEntry) * MAX_BACKEND_PROCESES);
for (i = 0; i < MAX_BACKEND_PROCESES; i++)
{
pgssWaitEventKey key;
pgssWaitEventEntry *entry = NULL;
bool found = false;
key.processid = i;
entry = (pgssWaitEventEntry *) hash_search(pgss_waiteventshash, &key, HASH_ENTER, &found);
if (!found)
{
SpinLockInit(&entry->mutex);
pgssWaitEventEntries[i] = entry;
}
}
pgssBucketEntries = malloc(sizeof (pgssBucketEntry) * PGSM_MAX_BUCKETS);
for (i = 0; i < PGSM_MAX_BUCKETS; i++)
{
pgssBucketHashKey key;
pgssBucketEntry *entry = NULL;
bool found = false;
key.bucket_id = i;
/* Find or create an entry with desired hash code */
entry = (pgssBucketEntry *) hash_search(pgss_buckethash, &key, HASH_ENTER, &found);
if (!found)
{
memset(&entry->counters, 0, sizeof(pgssBucketCounters));
SpinLockInit(&entry->mutex);
pgssBucketEntries[i] = entry;
}
}
LWLockRelease(AddinShmemInitLock);
/*
* If we're in the postmaster (or a standalone backend...), set up a shmem
* exit hook to dump the statistics to disk.
*/
if (!IsUnderPostmaster)
on_shmem_exit(pgss_shmem_shutdown, (Datum) 0);
}
/*
* shmem_shutdown hook: Dump statistics into file.
*
* Note: we don't bother with acquiring lock, because there should be no
* other processes running when this is called.
*/
static void
pgss_shmem_shutdown(int code, Datum arg)
{
elog(DEBUG2, "pg_stat_monitor: %s()", __FUNCTION__);
/* Don't try to dump during a crash. */
if (code)
return;
/* Safety check ... shouldn't get here unless shmem is set up. */
if (IsHashInitialize())
return;
}
/*
* Post-parse-analysis hook: mark query with a queryId
*/
@ -434,9 +259,7 @@ pgss_post_parse_analyze(ParseState *pstate, Query *query)
}
}
}
LWLockAcquire(pgss->lock, LW_EXCLUSIVE);
add_object_entry(query->queryId, tables_name);
LWLockRelease(pgss->lock);
}
/*
@ -446,7 +269,6 @@ pgss_post_parse_analyze(ParseState *pstate, Query *query)
if (query->queryId == UINT64CONST(0))
query->queryId = UINT64CONST(1);
if (PGSM_ENABLED == 1)
if (jstate.clocations_count > 0)
pgss_store(pstate->p_sourcetext,
@ -839,7 +661,11 @@ static void pgss_store(const char *query, uint64 queryId,
bool reset = false;
int i;
char tables_name[MAX_REL_LEN] = {0};
int len;
pgssSharedState *pgss = pgsm_get_ss();
pgssBucketEntry **pgssBucketEntries = pgsm_get_bucket();
HTAB *pgss_hash = pgsm_get_hash();
Assert(query != NULL);
Assert(PGSM_ENABLED);
@ -885,29 +711,15 @@ static void pgss_store(const char *query, uint64 queryId,
if (queryId == UINT64CONST(0))
queryId = pgss_hash_string(query, query_len);
{
pgssObjectHashKey key;
pgssObjectEntry *entry;
key.queryid = queryId;
LWLockAcquire(pgss->lock, LW_SHARED);
entry = (pgssObjectEntry *) hash_search(pgss_object_hash, &key, HASH_FIND, NULL);
if (entry != NULL)
{
LWLockRelease(pgss->lock);
LWLockAcquire(pgss->lock, LW_EXCLUSIVE);
snprintf(tables_name, MAX_REL_LEN, "%s", entry->tables_name);
hash_search(pgss_object_hash, &entry->key, HASH_REMOVE, NULL);
}
LWLockRelease(pgss->lock);
}
remove_object_entry(queryId, tables_name);
len = strlen(tables_name);
/* Set up key for hashtable search */
key.userid = GetUserId();
key.dbid = MyDatabaseId;
key.queryid = queryId;
key.ip = pg_get_client_addr();
key.bucket_id = get_next_wbucket(pgss);
if (key.bucket_id != pgss->current_wbucket)
@ -915,7 +727,6 @@ static void pgss_store(const char *query, uint64 queryId,
reset = true;
pgss->current_wbucket = key.bucket_id;
}
/* Lookup the hash table entry with shared lock. */
LWLockAcquire(pgss->lock, LW_SHARED);
entry = (pgssEntry *) hash_search(pgss_hash, &key, HASH_FIND, NULL);
@ -942,7 +753,7 @@ static void pgss_store(const char *query, uint64 queryId,
LWLockAcquire(pgss->lock, LW_EXCLUSIVE);
/* OK to create a new hashtable entry */
entry = entry_alloc(pgss, &key, 0, query_len, encoding, jstate != NULL);
entry = hash_entry_alloc(pgss, &key, encoding);
if (entry == NULL)
goto exit;
}
@ -968,11 +779,6 @@ static void pgss_store(const char *query, uint64 queryId,
if (reset)
memset(&entry->counters, 0, sizeof(Counters));
/* Calculate the agregates for database/user and host */
update_agg_counters(entry->key.bucket_id, key.queryid, key.dbid, AGG_KEY_DATABASE);
update_agg_counters(entry->key.bucket_id, key.queryid, key.userid, AGG_KEY_USER);
update_agg_counters(entry->key.bucket_id, key.queryid, pg_get_client_addr(), AGG_KEY_HOST);
/* "Unstick" entry if it was previously sticky */
if (e->counters.calls[kind].calls == 0)
e->counters.calls[kind].usage = USAGE_INIT;
@ -1033,7 +839,7 @@ static void pgss_store(const char *query, uint64 queryId,
e->counters.info.host = pg_get_client_addr();
e->counters.sysinfo.utime = utime;
e->counters.sysinfo.stime = stime;
for(i = 0; i < MAX_REL_LEN - 1; i++)
for(i = 0; i < len; i++)
e->counters.info.tables_name[i] = tables_name[i];
SpinLockRelease(&e->mutex);
}
@ -1053,11 +859,12 @@ exit:
Datum
pg_stat_monitor_reset(PG_FUNCTION_ARGS)
{
if (!pgss || !pgss_hash || !pgss_agghash || !pgss_buckethash || !pgss_waiteventshash)
/* Safety check... */
if (!IsHashInitialize())
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("pg_stat_monitor: must be loaded via shared_preload_libraries")));
entry_dealloc(-1);
hash_entry_dealloc(-1);
PG_RETURN_VOID();
}
@ -1066,19 +873,20 @@ pg_stat_monitor_reset(PG_FUNCTION_ARGS)
Datum
pg_stat_wait_events(PG_FUNCTION_ARGS)
{
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
TupleDesc tupdesc;
Tuplestorestate *tupstore;
MemoryContext per_query_ctx;
MemoryContext oldcontext;
HASH_SEQ_STATUS hash_seq;
pgssWaitEventEntry *entry;
char *query_txt;
char queryid_txt[64];
query_txt = (char*) malloc(PGSM_QUERY_MAX_LEN);
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
TupleDesc tupdesc;
Tuplestorestate *tupstore;
MemoryContext per_query_ctx;
MemoryContext oldcontext;
HASH_SEQ_STATUS hash_seq;
pgssWaitEventEntry *entry;
char *query_txt;
char queryid_txt[64];
pgssSharedState *pgss = pgsm_get_ss();
HTAB *pgss_waiteventshash = pgsm_get_wait_event_hash();
/* hash table must exist already */
if (!pgss || !pgss_hash || !pgss_object_hash)
/* Safety check... */
if (!IsHashInitialize())
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("pg_stat_monitor: must be loaded via shared_preload_libraries")));
@ -1094,6 +902,8 @@ pg_stat_wait_events(PG_FUNCTION_ARGS)
errmsg("pg_stat_monitor: materialize mode required, but it is not " \
"allowed in this context")));
query_txt = (char*) malloc(PGSM_QUERY_MAX_LEN);
/* Switch into long-lived context to construct returned data structures */
per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
oldcontext = MemoryContextSwitchTo(per_query_ctx);
@ -1161,7 +971,6 @@ pg_stat_wait_events(PG_FUNCTION_ARGS)
Datum
pg_stat_monitor(PG_FUNCTION_ARGS)
{
/* If it's really API 1.1, we'll figure that out below */
pg_stat_monitor_internal(fcinfo, true);
return (Datum) 0;
}
@ -1171,24 +980,28 @@ static void
pg_stat_monitor_internal(FunctionCallInfo fcinfo,
bool showtext)
{
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
TupleDesc tupdesc;
Tuplestorestate *tupstore;
MemoryContext per_query_ctx;
MemoryContext oldcontext;
Oid userid = GetUserId();
bool is_allowed_role = false;
HASH_SEQ_STATUS hash_seq;
pgssEntry *entry;
char *query_txt;
char queryid_txt[64];
query_txt = (char*) malloc(PGSM_QUERY_MAX_LEN);
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
TupleDesc tupdesc;
Tuplestorestate *tupstore;
MemoryContext per_query_ctx;
MemoryContext oldcontext;
Oid userid = GetUserId();
bool is_allowed_role = false;
HASH_SEQ_STATUS hash_seq;
pgssEntry *entry;
char *query_txt;
char queryid_txt[64];
pgssSharedState *pgss = pgsm_get_ss();
HTAB *pgss_hash = pgsm_get_hash();
pgssBucketEntry **pgssBucketEntries = pgsm_get_bucket_entries();
query_txt = (char*) malloc(PGSM_QUERY_MAX_LEN);
/* Superusers or members of pg_read_all_stats members are allowed */
is_allowed_role = is_member_of_role(GetUserId(), DEFAULT_ROLE_READ_ALL_STATS);
/* hash table must exist already */
if (!pgss || !pgss_hash || !pgss_object_hash)
/* Safety check... */
if (!IsHashInitialize())
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("pg_stat_monitor: must be loaded via shared_preload_libraries")));
@ -1243,6 +1056,7 @@ pg_stat_monitor_internal(FunctionCallInfo fcinfo,
values[i++] = ObjectIdGetDatum(entry->key.bucket_id);
values[i++] = ObjectIdGetDatum(entry->key.userid);
values[i++] = ObjectIdGetDatum(entry->key.dbid);
values[i++] = Int64GetDatumFast(entry->key.ip);
/* copy counters to a local variable to keep locking time short */
{
volatile pgssEntry *e = (volatile pgssEntry *) entry;
@ -1307,7 +1121,6 @@ pg_stat_monitor_internal(FunctionCallInfo fcinfo,
values[i++] = Int64GetDatumFast(tmp.blocks.temp_blks_written);
values[i++] = Float8GetDatumFast(tmp.blocks.blk_read_time);
values[i++] = Float8GetDatumFast(tmp.blocks.blk_write_time);
values[i++] = Int64GetDatumFast(tmp.info.host);
values[i++] = ArrayGetTextDatum(pgssBucketEntries[entry->key.bucket_id]->counters.resp_calls);
values[i++] = Float8GetDatumFast(tmp.sysinfo.utime);
values[i++] = Float8GetDatumFast(tmp.sysinfo.stime);
@ -1325,78 +1138,13 @@ pg_stat_monitor_internal(FunctionCallInfo fcinfo,
tuplestore_donestoring(tupstore);
}
/*
* Estimate shared memory space needed.
*/
static Size
pgss_memsize(void)
{
Size size;
size = MAXALIGN(sizeof(pgssSharedState));
size = add_size(size, hash_estimate_size(PGSM_MAX, sizeof(pgssEntry)));
return size;
}
/*
* Allocate a new hashtable entry.
* caller must hold an exclusive lock on pgss->lock
*
* "query" need not be null-terminated; we rely on query_len instead
*
* If "sticky" is true, make the new entry artificially sticky so that it will
* probably still be there when the query finishes execution. We do this by
* giving it a median usage value rather than the normal value. (Strictly
* speaking, query strings are normalized on a best effort basis, though it
* would be difficult to demonstrate this even under artificial conditions.)
*
* Note: despite needing exclusive lock, it's not an error for the target
* entry to already exist. This is because pgss_store releases and
* reacquires lock after failing to find a match; so someone else could
* have made the entry while we waited to get exclusive lock.
*/
static pgssEntry *
entry_alloc(pgssSharedState *pgss, pgssHashKey *key, Size query_offset, int query_len, int encoding,
bool sticky)
{
pgssEntry *entry = NULL;
bool found = false;
if (pgss->bucket_entry[pgss->current_wbucket] >= (PGSM_MAX / PGSM_MAX_BUCKETS))
{
pgss->bucket_overflow[pgss->current_wbucket]++;
return NULL;
}
if (hash_get_num_entries(pgss_hash) >= PGSM_MAX)
return NULL;
/* Find or create an entry with desired hash code */
entry = (pgssEntry *) hash_search(pgss_hash, key, HASH_ENTER, &found);
if (!found)
{
pgss->bucket_entry[pgss->current_wbucket]++;
/* New entry, initialize it */
/* reset the statistics */
memset(&entry->counters, 0, sizeof(Counters));
/* set the appropriate initial usage count */
entry->counters.calls[0].usage = sticky ? pgss->cur_median_usage : USAGE_INIT;
/* re-initialize the mutex each time ... we assume no one using it */
SpinLockInit(&entry->mutex);
/* ... and don't forget the query text metadata */
entry->encoding = encoding;
}
return entry;
}
static uint64
get_next_wbucket(pgssSharedState *pgss)
{
struct timeval tv;
uint64 current_usec;
uint64 bucket_id;
pgssBucketEntry **pgssBucketEntries = pgsm_get_bucket();
gettimeofday(&tv,NULL);
current_usec = tv.tv_sec;
@ -1408,7 +1156,7 @@ get_next_wbucket(pgssSharedState *pgss)
bucket_id = 0;
LWLockAcquire(pgss->lock, LW_EXCLUSIVE);
entry_dealloc(bucket_id);
hash_entry_dealloc(bucket_id);
/* reset the query buffer */
pgss->query_fifo[bucket_id].head = 0;
pgss->query_fifo[bucket_id].tail = 0;
@ -1422,94 +1170,6 @@ get_next_wbucket(pgssSharedState *pgss)
return pgss->current_wbucket;
}
/*
* Deallocate least-used entries.
*
* Caller must hold an exclusive lock on pgss->lock.
*/
static void
entry_dealloc(int bucket)
{
HASH_SEQ_STATUS hash_seq;
HASH_SEQ_STATUS hash_dbseq;
pgssEntry *entry;
pgssAggEntry *agg_entry;
pgssEntry **entries;
pgssAggEntry **agg_entries;
int i;
int nvictims = 0;
pgss->bucket_entry[bucket] = 0;
entries = palloc(hash_get_num_entries(pgss_hash) * sizeof(pgssEntry *));
hash_seq_init(&hash_seq, pgss_hash);
while ((entry = hash_seq_search(&hash_seq)) != NULL)
{
if (entry->key.bucket_id == bucket || bucket < 0)
entries[nvictims++] = entry;
}
for (i = 0; i < nvictims; i++)
entry = hash_search(pgss_hash, &entries[i]->key, HASH_REMOVE, NULL);
nvictims = 0;
agg_entries = palloc(hash_get_num_entries(pgss_agghash) * sizeof(pgssAggEntry *));
hash_seq_init(&hash_dbseq, pgss_agghash);
while ((agg_entry = hash_seq_search(&hash_dbseq)) != NULL)
{
if (agg_entry->key.bucket_id == bucket || bucket < 0)
agg_entries[nvictims++] = agg_entry;
}
for (i = 0; i < nvictims; i++)
hash_search(pgss_agghash, &agg_entries[i]->key, HASH_REMOVE, NULL);
pfree(entries);
pfree(agg_entries);
}
/*
* Release all entries.
*/
static void
entry_reset()
{
HASH_SEQ_STATUS hash_seq;
pgssEntry *entry;
pgssAggEntry *dbentry;
pgssObjectEntry *objentry;
pgssWaitEventEntry *weentry;
LWLockAcquire(pgss->lock, LW_EXCLUSIVE);
hash_seq_init(&hash_seq, pgss_hash);
while ((entry = hash_seq_search(&hash_seq)) != NULL)
{
hash_search(pgss_hash, &entry->key, HASH_REMOVE, NULL);
}
hash_seq_init(&hash_seq, pgss_agghash);
while ((dbentry = hash_seq_search(&hash_seq)) != NULL)
{
hash_search(pgss_agghash, &dbentry->key, HASH_REMOVE, NULL);
}
hash_seq_init(&hash_seq, pgss_buckethash);
while ((objentry = hash_seq_search(&hash_seq)) != NULL)
{
hash_search(pgss_buckethash, &dbentry->key, HASH_REMOVE, NULL);
}
hash_seq_init(&hash_seq, pgss_waiteventshash);
while ((weentry = hash_seq_search(&hash_seq)) != NULL)
{
hash_search(pgss_waiteventshash, &dbentry->key, HASH_REMOVE, NULL);
}
pgss->current_wbucket = 0;
free(pgssWaitEventEntries);
free(pgssBucketEntries);
LWLockRelease(pgss->lock);
}
/*
* AppendJumble: Append a value that is substantive in a given query to
* the current jumble.
@ -2441,151 +2101,6 @@ array_get_datum(int arr[])
return CStringGetTextDatum(str);
}
/* Alocate memory for a new entry */
void add_object_entry(uint64 queryid, char *objects)
{
pgssObjectEntry *entry = NULL;
bool found;
pgssObjectHashKey key;
key.queryid = queryid;
entry = (pgssObjectEntry *) hash_search(pgss_object_hash, &key, HASH_ENTER, &found);
if (!found)
{
SpinLockAcquire(&entry->mutex);
snprintf(entry->tables_name, MAX_REL_LEN, "%s", objects);
SpinLockRelease(&entry->mutex);
}
}
/* Alocate memory for a new entry */
static pgssAggEntry *
agg_entry_alloc(pgssAggHashKey *key)
{
pgssAggEntry *entry = NULL;
bool found;
entry = (pgssAggEntry *) hash_search(pgss_agghash, key, HASH_ENTER, &found);
if (!found)
{
SpinLockAcquire(&entry->mutex);
memset(&entry->counters, 0, sizeof(pgssAggCounters));
entry->counters.total_calls = 0;
SpinLockRelease(&entry->mutex);
}
return entry;
}
static void
update_agg_counters(uint64 bucket, uint64 queryid, uint64 id, AGG_KEY type)
{
pgssAggHashKey key;
pgssAggEntry *entry;
key.id = id;
key.type = (int64) type;
key.queryid = queryid;
key.bucket_id = bucket;
entry = agg_entry_alloc(&key);
if (!entry)
return;
SpinLockAcquire(&entry->mutex);
entry->key.queryid = queryid;
entry->key.id = id;
entry->key.type = key.type;
entry->key.bucket_id = bucket;
entry->counters.total_calls++;
SpinLockRelease(&entry->mutex);
}
Datum
pg_stat_agg(PG_FUNCTION_ARGS)
{
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
TupleDesc tupdesc;
Tuplestorestate *tupstore;
MemoryContext per_query_ctx;
MemoryContext oldcontext;
HASH_SEQ_STATUS hash_seq;
pgssAggEntry *entry;
/* hash table must exist already */
if (!pgss || !pgss_agghash)
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("pg_stat_monitor: must be loaded via shared_preload_libraries")));
/* check to see if caller supports us returning a tuplestore */
if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("pg_stat_monitor: set-valued function called in context that cannot accept a set")));
if (!(rsinfo->allowedModes & SFRM_Materialize))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("pg_stat_monitor: materialize mode required, but it is not " \
"allowed in this context")));
/* Switch into long-lived context to construct returned data structures */
per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
oldcontext = MemoryContextSwitchTo(per_query_ctx);
/* Build a tuple descriptor for our result type */
if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
elog(ERROR, "pg_stat_monitor: return type must be a row type");
if (tupdesc->natts != 4)
elog(ERROR, "pg_stat_monitor: incorrect number of output arguments, required %d", tupdesc->natts);
tupstore = tuplestore_begin_heap(true, false, work_mem);
rsinfo->returnMode = SFRM_Materialize;
rsinfo->setResult = tupstore;
rsinfo->setDesc = tupdesc;
MemoryContextSwitchTo(oldcontext);
/*
* Get shared lock, load or reload the query text file if we must, and
* iterate over the hashtable entries.
*
* With a large hash table, we might be holding the lock rather longer
* than one could wish. However, this only blocks creation of new hash
* table entries, and the larger the hash table the less likely that is to
* be needed. So we can hope this is okay. Perhaps someday we'll decide
* we need to partition the hash table to limit the time spent holding any
* one lock.
*/
LWLockAcquire(pgss->lock, LW_SHARED);
hash_seq_init(&hash_seq, pgss_agghash);
while ((entry = hash_seq_search(&hash_seq)) != NULL)
{
Datum values[6];
bool nulls[6];
int i = 0;
char queryid_txt[32];
memset(values, 0, sizeof(values));
memset(nulls, 0, sizeof(nulls));
sprintf(queryid_txt, "%08lX", entry->key.queryid);
values[i++] = CStringGetTextDatum(queryid_txt);
values[i++] = Int64GetDatumFast(entry->key.id);
values[i++] = Int64GetDatumFast(entry->key.type);
values[i++] = Int64GetDatumFast(entry->counters.total_calls);
tuplestore_putvalues(tupstore, tupdesc, values, nulls);
}
/* clean up and return the tuplestore */
LWLockRelease(pgss->lock);
tuplestore_donestoring(tupstore);
return 0;
}
#define FIFO_HEAD(b) pgss->query_fifo[b].head
#define FIFO_TAIL(b) pgss->query_fifo[b].tail
@ -2595,6 +2110,8 @@ locate_query(uint64 bucket_id, uint64 queryid, char * query)
uint64 id = 0;
uint64 len = 0;
uint64 offset = 0;
pgssSharedState *pgss = pgsm_get_ss();
uint64 tail = FIFO_TAIL(bucket_id);
unsigned char *buf = pgss_qbuf[bucket_id];
@ -2619,7 +2136,7 @@ locate_query(uint64 bucket_id, uint64 queryid, char * query)
if (id == queryid)
return id;
tail = (tail + offset) % query_buf_size_bucket;
tail = (tail + offset) % pgsm_get_bucket_size();
}
return 0;
}
@ -2627,8 +2144,9 @@ locate_query(uint64 bucket_id, uint64 queryid, char * query)
static void
store_query(uint64 queryid, const char *query, uint64 query_len)
{
int next;
int offset = 0;
int next;
int offset = 0;
pgssSharedState *pgss = pgsm_get_ss();
if (query_len > PGSM_QUERY_MAX_LEN)
query_len = PGSM_QUERY_MAX_LEN;
@ -2640,7 +2158,7 @@ store_query(uint64 queryid, const char *query, uint64 query_len)
return;
next = FIFO_HEAD(pgss->current_wbucket) + query_len + sizeof (uint64) + sizeof (uint64);
if (next >= query_buf_size_bucket)
if (next >= pgsm_get_bucket_size())
next = 0;
/* Buffer is full */
@ -2669,6 +2187,8 @@ static PlannedStmt *pgss_planner_hook(Query *parse, int opt, ParamListInfo param
#endif
{
PlannedStmt *result;
pgssWaitEventEntry **pgssWaitEventEntries = pgsm_get_wait_event_entry();
if (MyProc)
{
int i = MyProc - ProcGlobal->allProcs;
@ -2751,8 +2271,9 @@ static PlannedStmt *pgss_planner_hook(Query *parse, int opt, ParamListInfo param
static void
update_wait_event(void)
{
PGPROC *proc = NULL;
int i;
PGPROC *proc = NULL;
int i;
pgssWaitEventEntry **pgssWaitEventEntries = pgsm_get_wait_event_entry();
LWLockAcquire(ProcArrayLock, LW_SHARED);
for (i = 0; i < ProcGlobal->allProcCount; i++)

View File

@ -36,8 +36,6 @@
#include "utils/lsyscache.h"
#include "utils/guc.h"
#define IsHashInitialize() (pgss || pgss_hash || pgss_object_hash || pgss_agghash || pgss_buckethash || pgss_waiteventshash)
#define MAX_BACKEND_PROCESES (MaxBackends + NUM_AUXILIARY_PROCS + max_prepared_xacts)
/* Time difference in miliseconds */
@ -131,42 +129,21 @@ typedef struct pgssObjectEntry
slock_t mutex; /* protects the counters only */
} pgssObjectEntry;
/* Aggregate shared memory storage */
typedef struct pgssAggHashKey
{
uint64 id; /* dbid, userid or ip depend upon the type */
uint64 type; /* type of id dbid, userid or ip */
uint64 queryid; /* query identifier, foreign key to the query */
uint64 bucket_id; /* bucket_id is the foreign key to pgssBucketHashKey */
} pgssAggHashKey;
typedef struct pgssAggCounters
{
uint64 total_calls; /* number of quries per database/user/ip */
} pgssAggCounters;
typedef struct pgssAggEntry
{
pgssAggHashKey key; /* hash key of entry - MUST BE FIRST */
pgssAggCounters counters; /* the statistics aggregates */
slock_t mutex; /* protects the counters only */
} pgssAggEntry;
typedef struct pgssWaitEventKey
{
uint64 queryid;
uint64 processid;
} pgssWaitEventKey;
#define MAX_QUERY_LEN 1024
typedef struct pgssWaitEventEntry
{
pgssAggHashKey key; /* hash key of entry - MUST BE FIRST */
uint64 queryid;
uint64 pid;
uint32 wait_event_info;
char query[MAX_QUERY_LEN];
slock_t mutex; /* protects the counters only */
pgssWaitEventKey key; /* hash key of entry - MUST BE FIRST */
uint64 queryid;
uint64 pid;
uint32 wait_event_info;
char query[MAX_QUERY_LEN];
slock_t mutex; /* protects the counters only */
} pgssWaitEventEntry;
@ -177,6 +154,7 @@ typedef struct pgssHashKey
uint64 queryid; /* query identifier */
Oid userid; /* user OID */
Oid dbid; /* database OID */
uint32 ip; /* client ip address */
} pgssHashKey;
typedef struct QueryInfo
@ -273,6 +251,7 @@ typedef struct pgssSharedState
uint64 bucket_overflow[MAX_BUCKETS];
uint64 bucket_entry[MAX_BUCKETS];
QueryFifo query_fifo[MAX_BUCKETS];
int query_buf_size_bucket;
} pgssSharedState;
#define ResetSharedState(x) \
@ -325,9 +304,31 @@ typedef struct pgssJumbleState
int highest_extern_param_id;
} pgssJumbleState;
/* Links to shared memory state */
/* guc.c */
void init_guc(void);
/* hash_create.c */
void add_object_entry(uint64 queryid, char *objects);
void remove_object_entry(uint64 queryid, char *objects);
bool IsHashInitialize(void);
void pgss_shmem_startup(void);
void pgss_shmem_shutdown(int code, Datum arg);
shmem_startup_hook_type prev_shmem_startup_hook;
int pgsm_get_bucket_size(void);
pgssSharedState* pgsm_get_ss(void);
pgssBucketEntry** pgsm_get_bucket_entries(void);
HTAB* pgsm_get_wait_event_hash(void);
pgssBucketEntry** pgsm_get_bucket(void);
HTAB* pgsm_get_hash(void);
pgssWaitEventEntry** pgsm_get_wait_event_entry(void);
void hash_entry_reset(void);
void hash_entry_dealloc(int bucket);
pgssEntry* hash_entry_alloc(pgssSharedState *pgss, pgssHashKey *key, int encoding);
Size hash_memsize(void);
pgssEntry* pgsm_create_query_entry(unsigned int queryid, unsigned int userid, unsigned int dbid, unsigned int bucket_id, unsigned int ip);
/*---- GUC variables ----*/
#define PGSM_MAX conf[0].guc_variable
#define PGSM_QUERY_MAX_LEN conf[1].guc_variable
@ -336,10 +337,10 @@ void init_guc(void);
#define PGSM_NORMALIZED_QUERY conf[4].guc_variable
#define PGSM_MAX_BUCKETS conf[5].guc_variable
#define PGSM_BUCKET_TIME conf[6].guc_variable
#define PGSM_QUERY_BUF_SIZE conf[7].guc_variable
#define PGSM_OBJECT_CACHE conf[8].guc_variable
#define PGSM_RESPOSE_TIME_LOWER_BOUND conf[9].guc_variable
#define PGSM_RESPOSE_TIME_STEP conf[10].guc_variable
#define PGSM_OBJECT_CACHE conf[7].guc_variable
#define PGSM_RESPOSE_TIME_LOWER_BOUND conf[8].guc_variable
#define PGSM_RESPOSE_TIME_STEP conf[9].guc_variable
#define PGSM_QUERY_BUF_SIZE conf[10].guc_variable
#define PGSM_TRACK_PLANNING conf[11].guc_variable
GucVariable conf[12];