diff --git a/expected/pg_stat_monitor.out b/expected/pg_stat_monitor.out index b5e6127..3fbd635 100644 --- a/expected/pg_stat_monitor.out +++ b/expected/pg_stat_monitor.out @@ -99,13 +99,13 @@ SELECT query, calls, rows FROM pg_stat_monitor ORDER BY query COLLATE "C"; query | calls | rows ---------------------------------------------------+-------+------ PREPARE pgss_test (int) AS SELECT $1, $2 LIMIT $3 | 1 | 1 + SELECT $1 | 2 | 2 SELECT $1 +| 4 | 4 +| | AS "text" | | SELECT $1 + $2 | 2 | 2 SELECT $1 + $2 + $3 AS "add" | 3 | 3 SELECT $1 AS "float" | 1 | 1 - SELECT $1 AS "int" | 2 | 2 SELECT $1 AS i UNION SELECT $2 ORDER BY i | 1 | 2 SELECT $1 || $2 | 1 | 1 SELECT pg_stat_monitor_reset() | 1 | 1 @@ -232,9 +232,11 @@ SELECT 1 + 1 AS "two"; (1 row) SELECT query, calls, rows FROM pg_stat_monitor ORDER BY query COLLATE "C"; - query | calls | rows --------+-------+------ -(0 rows) + query | calls | rows +----------------+-------+------ + SELECT $1 | 0 | 0 + SELECT $1 + $2 | 0 | 0 +(2 rows) -- -- pg_stat_monitor.track = top @@ -288,15 +290,17 @@ SELECT PLUS_ONE(10); (1 row) SELECT query, calls, rows FROM pg_stat_monitor ORDER BY query COLLATE "C"; - query | calls | rows ---------------------------------+-------+------ - SELECT $1 +| 1 | 1 - +| | - AS "text" | | - SELECT PLUS_ONE($1) | 2 | 2 - SELECT PLUS_TWO($1) | 2 | 2 - SELECT pg_stat_monitor_reset() | 1 | 1 -(4 rows) + query | calls | rows +-----------------------------------+-------+------ + SELECT $1 +| 1 | 1 + +| | + AS "text" | | + SELECT (i + $2 + $3)::INTEGER | 0 | 0 + SELECT (i + $2)::INTEGER LIMIT $3 | 0 | 0 + SELECT PLUS_ONE($1) | 2 | 2 + SELECT PLUS_TWO($1) | 2 | 2 + SELECT pg_stat_monitor_reset() | 1 | 1 +(6 rows) -- -- pg_stat_monitor.track = all @@ -393,7 +397,7 @@ SELECT query, calls, rows FROM pg_stat_monitor ORDER BY query COLLATE "C"; DROP FUNCTION PLUS_TWO(INTEGER) | 1 | 0 DROP TABLE IF EXISTS test | 3 | 0 DROP TABLE test | 1 | 0 - SELECT $1 AS "int" | 1 | 1 + SELECT $1 | 1 | 1 SELECT pg_stat_monitor_reset() | 1 | 1 (8 rows) diff --git a/guc.c b/guc.c index c9fb5b7..dbf053c 100644 --- a/guc.c +++ b/guc.c @@ -53,6 +53,7 @@ init_guc(void) .guc_max = 0, .guc_restart = false }; + conf[i++] = (GucVariable) { .guc_name = "pg_stat_monitor.pgsm_normalized_query", .guc_desc = "Selects whether save query in normalized format.", @@ -112,6 +113,16 @@ init_guc(void) .guc_max = INT_MAX, .guc_restart = true }; +#if PG_VERSION_NUM >= 130000 + conf[i++] = (GucVariable) { + .guc_name = "pg_stat_monitor.pgsm_track_planning", + .guc_desc = "Selects whether planning statistics are tracked.", + .guc_default = 1, + .guc_min = 0, + .guc_max = 0, + .guc_restart = false + }; +#endif DefineCustomIntVariable("pg_stat_monitor.max", "Sets the maximum number of statements tracked by pg_stat_monitor.", @@ -173,7 +184,7 @@ init_guc(void) NULL, NULL); - DefineCustomIntVariable("pg_stat_monitor.PGSM_MAX_buckets ", + DefineCustomIntVariable("pg_stat_monitor.pgsm_max_buckets ", "Sets the maximum number of buckets.", NULL, &PGSM_MAX_BUCKETS, @@ -239,7 +250,7 @@ init_guc(void) NULL, NULL); - DefineCustomIntVariable("pg_stat_monitor.query_shared_buffer", + DefineCustomIntVariable("pg_stat_monitor.pgsm_query_shared_buffer.", "Sets the shared_buffer size", NULL, &PGSM_QUERY_BUF_SIZE, @@ -251,6 +262,16 @@ init_guc(void) NULL, NULL, NULL); + DefineCustomBoolVariable("pg_stat_monitor.pgsm_track_planning", + "Selects whether track planning statistics.", + NULL, + (bool*)&PGSM_TRACK_PLANNING, + true, + PGC_SUSET, + 0, + NULL, + NULL, + NULL); } diff --git a/pg_stat_monitor--1.0.sql b/pg_stat_monitor--1.0.sql index c02d23b..f681ebc 100644 --- a/pg_stat_monitor--1.0.sql +++ b/pg_stat_monitor--1.0.sql @@ -17,14 +17,25 @@ CREATE FUNCTION pg_stat_monitor(IN showtext boolean, OUT queryid text, OUT query text, OUT bucket_start_time timestamptz, - OUT calls 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 shared_blks_hit int8, + + OUT shared_blks_hit int8, OUT shared_blks_read int8, OUT shared_blks_dirtied int8, OUT shared_blks_written int8, @@ -96,13 +107,20 @@ CREATE VIEW pg_stat_monitor AS SELECT dbid, m.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, + plan_rows, calls, - total_time, - min_time, - max_time, - mean_time, - stddev_time, - rows, + 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, shared_blks_hit, shared_blks_read, shared_blks_dirtied, diff --git a/pg_stat_monitor.c b/pg_stat_monitor.c index bbf21b9..08666d3 100644 --- a/pg_stat_monitor.c +++ b/pg_stat_monitor.c @@ -25,6 +25,10 @@ void _PG_fini(void); /* 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; @@ -77,10 +81,10 @@ 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 +static void BufferUsageAccumDiff(BufferUsage* bufusage, BufferUsage* pgBufferUsage, BufferUsage* bufusage_start); static PlannedStmt *pgss_planner_hook(Query *parse, int opt, ParamListInfo param); #endif @@ -91,21 +95,34 @@ static void pgss_ExecutorStart(QueryDesc *queryDesc, int eflags); static void pgss_ExecutorRun(QueryDesc *queryDesc, ScanDirection direction, uint64 count, bool execute_once); static void pgss_ExecutorFinish(QueryDesc *queryDesc); static void pgss_ExecutorEnd(QueryDesc *queryDesc); + +#if PG_VERSION_NUM >= 130000 +static void pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString, + ProcessUtilityContext context, + ParamListInfo params, QueryEnvironment *queryEnv, + DestReceiver *dest, + QueryCompletion *qc + ); +#else static void pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString, ProcessUtilityContext context, ParamListInfo params, QueryEnvironment *queryEnv, DestReceiver *dest, -#if PG_VERSION_NUM >= 130000 - QueryCompletion *qc); -#else char *completionTag); #endif + static uint64 pgss_hash_string(const char *str, int len); static void pgss_store(const char *query, uint64 queryId, - int query_location, int query_len, - double total_time, uint64 rows, - const BufferUsage *bufusage, float utime, float stime, - pgssJumbleState *jstate); + int query_location, int query_len, + bool kind, + double total_time, uint64 rows, + const BufferUsage *bufusage, +#if PG_VERSION_NUM >= 130000 + const WalUsage *walusage, +#endif + pgssJumbleState *jstate, + float utime, float stime); + static void pg_stat_monitor_internal(FunctionCallInfo fcinfo, bool showtext); static Size pgss_memsize(void); @@ -232,10 +249,6 @@ pgss_shmem_startup(void) bool found = false; int32 i; - elog(WARNING, "pg_stat_monitor: %s()", __FUNCTION__); - - Assert(IsHashInitialize()); - if (prev_shmem_startup_hook) prev_shmem_startup_hook(); @@ -290,7 +303,7 @@ pgss_shmem_startup(void) sizeof(pgssAggEntry), PGSM_MAX * 3); - Assert(!IsHashInitialize()); + Assert(IsHashInitialize()); pgssWaitEventEntries = malloc(sizeof (pgssWaitEventEntry) * MAX_BACKEND_PROCESES); for (i = 0; i < MAX_BACKEND_PROCESES; i++) @@ -372,7 +385,7 @@ pgss_post_parse_analyze(ParseState *pstate, Query *query) Assert(query->queryId == UINT64CONST(0)); /* Safety check... */ - if (IsHashInitialize()) + if (!IsHashInitialize()) return; /* @@ -438,12 +451,16 @@ pgss_post_parse_analyze(ParseState *pstate, Query *query) query->queryId, query->stmt_location, query->stmt_len, + PGSS_INVALID, 0, 0, NULL, - 0, - 0, - &jstate); +#if PG_VERSION_NUM >= 130000 + NULL, +#endif + &jstate, + 0.0, + 0.0); } /* @@ -535,9 +552,9 @@ pgss_ExecutorFinish(QueryDesc *queryDesc) static void pgss_ExecutorEnd(QueryDesc *queryDesc) { + float utime; + float stime; uint64 queryId = queryDesc->plannedstmt->queryId; - float utime; - float stime; if (queryId != UINT64CONST(0) && queryDesc->totaltime && PGSS_ENABLED()) { @@ -549,16 +566,21 @@ pgss_ExecutorEnd(QueryDesc *queryDesc) getrusage(RUSAGE_SELF, &rusage_end); utime = TIMEVAL_DIFF(rusage_start.ru_utime, rusage_end.ru_utime); stime = TIMEVAL_DIFF(rusage_start.ru_stime, rusage_end.ru_stime); + pgss_store(queryDesc->sourceText, queryId, queryDesc->plannedstmt->stmt_location, queryDesc->plannedstmt->stmt_len, + PGSS_EXEC, queryDesc->totaltime->total * 1000.0, /* convert to msec */ queryDesc->estate->es_processed, &queryDesc->totaltime->bufusage, +#if PG_VERSION_NUM >= 130000 + &queryDesc->totaltime->walusage, +#endif + NULL, utime, - stime, - NULL); + stime); } if (prev_ExecutorEnd) @@ -571,15 +593,18 @@ pgss_ExecutorEnd(QueryDesc *queryDesc) /* * ProcessUtility hook */ -static void -pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString, - ProcessUtilityContext context, - ParamListInfo params, QueryEnvironment *queryEnv, - DestReceiver *dest, #if PG_VERSION_NUM >= 130000 - QueryCompletion *qc) +static void pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString, + ProcessUtilityContext context, + ParamListInfo params, QueryEnvironment *queryEnv, + DestReceiver *dest, + QueryCompletion *qc) #else - char *completionTag) +static void pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString, + ProcessUtilityContext context, ParamListInfo params, + QueryEnvironment *queryEnv, + DestReceiver *dest, + char *completionTag) #endif { Node *parsetree = pstmt->utilityStmt; @@ -598,7 +623,7 @@ pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString, * * Likewise, we don't track execution of DEALLOCATE. */ - if (PGSM_TRACK_UTILITY && PGSS_ENABLED() && + if (PGSM_TRACK_UTILITY && !IsA(parsetree, ExecuteStmt) && !IsA(parsetree, PrepareStmt) && !IsA(parsetree, DeallocateStmt)) @@ -608,42 +633,61 @@ pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString, uint64 rows; BufferUsage bufusage_start, bufusage; +#if PG_VERSION_NUM >= 130000 + WalUsage walusage_start, + walusage; + walusage_start = pgWalUsage; + exec_nested_level++; +#endif bufusage_start = pgBufferUsage; INSTR_TIME_SET_CURRENT(start); - nested_level++; PG_TRY(); { if (prev_ProcessUtility) prev_ProcessUtility(pstmt, queryString, context, params, queryEnv, + dest #if PG_VERSION_NUM >= 130000 - dest, qc); + ,qc #else - dest, completionTag); + ,completionTag #endif + ); else standard_ProcessUtility(pstmt, queryString, context, params, queryEnv, + dest #if PG_VERSION_NUM >= 130000 - dest, qc); + ,qc #else - dest, completionTag); + ,completionTag #endif - nested_level--; + ); } - PG_CATCH(); +#if PG_VERSION_NUM >= 130000 + PG_FINALLY(); { + exec_nested_level--; + } +#else + PG_CATCH(); + { nested_level--; PG_RE_THROW(); - } - PG_END_TRY(); + } +#endif + PG_END_TRY(); INSTR_TIME_SET_CURRENT(duration); INSTR_TIME_SUBTRACT(duration, start); + #if PG_VERSION_NUM >= 130000 rows = (qc && qc->commandTag == CMDTAG_COPY) ? qc->nprocessed : 0; + /* calc differences of WAL counters. */ + memset(&walusage, 0, sizeof(WalUsage)); + WalUsageAccumDiff(&walusage, &pgWalUsage, &walusage_start); #else /* parse command tag to retrieve the number of affected rows. */ if (completionTag && strncmp(completionTag, "COPY ", 5) == 0) @@ -653,64 +697,69 @@ pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString, #endif /* calc differences of buffer counters. */ - bufusage.shared_blks_hit = - pgBufferUsage.shared_blks_hit - bufusage_start.shared_blks_hit; - bufusage.shared_blks_read = - pgBufferUsage.shared_blks_read - bufusage_start.shared_blks_read; - bufusage.shared_blks_dirtied = - pgBufferUsage.shared_blks_dirtied - bufusage_start.shared_blks_dirtied; - bufusage.shared_blks_written = - pgBufferUsage.shared_blks_written - bufusage_start.shared_blks_written; - bufusage.local_blks_hit = - pgBufferUsage.local_blks_hit - bufusage_start.local_blks_hit; - bufusage.local_blks_read = - pgBufferUsage.local_blks_read - bufusage_start.local_blks_read; - bufusage.local_blks_dirtied = - pgBufferUsage.local_blks_dirtied - bufusage_start.local_blks_dirtied; - bufusage.local_blks_written = - pgBufferUsage.local_blks_written - bufusage_start.local_blks_written; - bufusage.temp_blks_read = - pgBufferUsage.temp_blks_read - bufusage_start.temp_blks_read; - bufusage.temp_blks_written = - pgBufferUsage.temp_blks_written - bufusage_start.temp_blks_written; - bufusage.blk_read_time = pgBufferUsage.blk_read_time; - INSTR_TIME_SUBTRACT(bufusage.blk_read_time, bufusage_start.blk_read_time); - bufusage.blk_write_time = pgBufferUsage.blk_write_time; - INSTR_TIME_SUBTRACT(bufusage.blk_write_time, bufusage_start.blk_write_time); + memset(&bufusage, 0, sizeof(BufferUsage)); + BufferUsageAccumDiff(&bufusage, &pgBufferUsage, &bufusage_start); pgss_store(queryString, 0, /* signal that it's a utility stmt */ pstmt->stmt_location, pstmt->stmt_len, + PGSS_EXEC, INSTR_TIME_GET_MILLISEC(duration), rows, &bufusage, +#if PG_VERSION_NUM >= 130000 + &walusage, +#endif + NULL, 0, - 0, - NULL); + 0); } else { - if (prev_ProcessUtility) - prev_ProcessUtility(pstmt, queryString, - context, params, queryEnv, -#if PG_VERSION_NUM >= 130000 - dest, qc); -#else - dest, completionTag); -#endif - - else - standard_ProcessUtility(pstmt, queryString, + if (prev_ProcessUtility) + prev_ProcessUtility(pstmt, queryString, context, params, queryEnv, + dest #if PG_VERSION_NUM >= 130000 - dest, qc); + ,qc #else - dest, completionTag); + ,completionTag #endif + ); + standard_ProcessUtility(pstmt, queryString, + context, params, queryEnv, + dest +#if PG_VERSION_NUM >= 130000 + ,qc +#else + ,completionTag +#endif + ); } } +#if PG_VERSION_NUM < 130000 +static void +BufferUsageAccumDiff(BufferUsage* bufusage, BufferUsage* pgBufferUsage, BufferUsage* bufusage_start) +{ + /* calc differences of buffer counters. */ + bufusage->shared_blks_hit = pgBufferUsage->shared_blks_hit - bufusage_start->shared_blks_hit; + bufusage->shared_blks_read = pgBufferUsage->shared_blks_read - bufusage_start->shared_blks_read; + bufusage->shared_blks_dirtied = pgBufferUsage->shared_blks_dirtied - bufusage_start->shared_blks_dirtied; + bufusage->shared_blks_written = pgBufferUsage->shared_blks_written - bufusage_start->shared_blks_written; + bufusage->local_blks_hit = pgBufferUsage->local_blks_hit - bufusage_start->local_blks_hit; + bufusage->local_blks_read = pgBufferUsage->local_blks_read - bufusage_start->local_blks_read; + bufusage->local_blks_dirtied = pgBufferUsage->local_blks_dirtied - bufusage_start->local_blks_dirtied; + bufusage->local_blks_written = pgBufferUsage->local_blks_written - bufusage_start->local_blks_written; + bufusage->temp_blks_read = pgBufferUsage->temp_blks_read - bufusage_start->temp_blks_read; + bufusage->temp_blks_written = pgBufferUsage->temp_blks_written - bufusage_start->temp_blks_written; + bufusage->blk_read_time = pgBufferUsage->blk_read_time; + INSTR_TIME_SUBTRACT(bufusage->blk_read_time, bufusage_start->blk_read_time); + bufusage->blk_write_time = pgBufferUsage->blk_write_time; + INSTR_TIME_SUBTRACT(bufusage->blk_write_time, bufusage_start->blk_write_time); +} +#endif /* * Given an arbitrarily long query string, produce a hash for the purposes of * identifying the query, without normalizing constants. Used when hashing @@ -769,12 +818,16 @@ pg_get_client_addr(void) * we have no statistics as yet; we just want to record the normalized * query string. total_time, rows, bufusage are ignored in this case. */ -static void -pgss_store(const char *query, uint64 queryId, - int query_location, int query_len, - double total_time, uint64 rows, - const BufferUsage *bufusage, - float utime, float stime, pgssJumbleState *jstate) +static void pgss_store(const char *query, uint64 queryId, + int query_location, int query_len, + bool kind, + double total_time, uint64 rows, + const BufferUsage *bufusage, +#if PG_VERSION_NUM >= 130000 + const WalUsage *walusage, +#endif + pgssJumbleState *jstate, + float utime, float stime) { pgssHashKey key; pgssEntry *entry; @@ -787,7 +840,7 @@ pgss_store(const char *query, uint64 queryId, Assert(query != NULL); /* Safety check... */ - if (IsHashInitialize() || !pgss_qbuf[pgss->current_wbucket]) + if (!IsHashInitialize() || !pgss_qbuf[pgss->current_wbucket]) return; /* @@ -917,16 +970,16 @@ pgss_store(const char *query, uint64 queryId, 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.calls == 0) - e->counters.calls.usage = USAGE_INIT; + if (e->counters.calls[kind].calls == 0) + e->counters.calls[kind].usage = USAGE_INIT; + e->counters.calls[kind].calls += 1; + e->counters.time[kind].total_time += total_time; - e->counters.calls.calls += 1; - e->counters.time.total_time += total_time; - if (e->counters.calls.calls == 1) + if (e->counters.calls[kind].calls == 1) { - e->counters.time.min_time = total_time; - e->counters.time.max_time = total_time; - e->counters.time.mean_time = total_time; + e->counters.time[kind].min_time = total_time; + e->counters.time[kind].max_time = total_time; + e->counters.time[kind].mean_time = total_time; } else { @@ -934,18 +987,18 @@ pgss_store(const char *query, uint64 queryId, * Welford's method for accurately computing variance. See * */ - double old_mean = e->counters.time.mean_time; + double old_mean = e->counters.time[kind].mean_time; - e->counters.time.mean_time += - (total_time - old_mean) / e->counters.calls.calls; - e->counters.time.sum_var_time += - (total_time - old_mean) * (total_time - e->counters.time.mean_time); + e->counters.time[kind].mean_time += + (total_time - old_mean) / e->counters.calls[kind].calls; + e->counters.time[kind].sum_var_time += + (total_time - old_mean) * (total_time - e->counters.time[kind].mean_time); /* calculate min and max time */ - if (e->counters.time.min_time > total_time) - e->counters.time.min_time = total_time; - if (e->counters.time.max_time < total_time) - e->counters.time.max_time = total_time; + if (e->counters.time[kind].min_time > total_time) + e->counters.time[kind].min_time = total_time; + if (e->counters.time[kind].max_time < total_time) + e->counters.time[kind].max_time = total_time; } for (i = 0; i < MAX_RESPONSE_BUCKET - 1; i++) @@ -959,7 +1012,7 @@ pgss_store(const char *query, uint64 queryId, if (total_time > PGSM_RESPOSE_TIME_LOWER_BOUND + (PGSM_RESPOSE_TIME_STEP * MAX_RESPONSE_BUCKET)) pgssBucketEntries[entry->key.bucket_id]->counters.resp_calls[MAX_RESPONSE_BUCKET - 1]++; - e->counters.calls.rows += rows; + e->counters.calls[kind].rows += rows; e->counters.blocks.shared_blks_hit += bufusage->shared_blks_hit; e->counters.blocks.shared_blks_read += bufusage->shared_blks_read; e->counters.blocks.shared_blks_dirtied += bufusage->shared_blks_dirtied; @@ -972,7 +1025,7 @@ pgss_store(const char *query, uint64 queryId, e->counters.blocks.temp_blks_written += bufusage->temp_blks_written; e->counters.blocks.blk_read_time += INSTR_TIME_GET_MILLISEC(bufusage->blk_read_time); e->counters.blocks.blk_write_time += INSTR_TIME_GET_MILLISEC(bufusage->blk_write_time); - e->counters.calls.usage += USAGE_EXEC(total_time); + e->counters.calls[kind].usage += USAGE_EXEC(total_time); e->counters.info.host = pg_get_client_addr(); e->counters.sysinfo.utime = utime; e->counters.sysinfo.stime = stime; @@ -1004,7 +1057,7 @@ pg_stat_monitor_reset(PG_FUNCTION_ARGS) PG_RETURN_VOID(); } -#define PG_STAT_STATEMENTS_COLS 31 /* maximum of above */ +#define PG_STAT_STATEMENTS_COLS 38 /* maximum of above */ Datum pg_stat_wait_events(PG_FUNCTION_ARGS) @@ -1221,30 +1274,22 @@ pg_stat_monitor_internal(FunctionCallInfo fcinfo, nulls[i++] = true; } - /* Skip entry if unexecuted (ie, it's a pending "sticky" entry) */ - if (tmp.calls.calls == 0) - continue; - values[i++] = TimestampGetDatum(pgssBucketEntries[entry->key.bucket_id]->counters.current_time); - values[i++] = Int64GetDatumFast(tmp.calls.calls); - values[i++] = Float8GetDatumFast(tmp.time.total_time); - values[i++] = Float8GetDatumFast(tmp.time.min_time); - values[i++] = Float8GetDatumFast(tmp.time.max_time); - values[i++] = Float8GetDatumFast(tmp.time.mean_time); - /* - * Note we are calculating the population variance here, not the - * sample variance, as we have data for the whole population, so - * Bessel's correction is not used, and we don't divide by - * tmp.calls - 1. - */ - if (tmp.calls.calls > 1) - stddev = sqrt(tmp.time.sum_var_time / tmp.calls.calls); - else - stddev = 0.0; - - values[i++] = Float8GetDatumFast(stddev); - values[i++] = Int64GetDatumFast(tmp.calls.rows); + for (int kind = 0; kind < PGSS_NUMKIND; kind++) + { + values[i++] = Int64GetDatumFast(tmp.calls[kind].calls); + values[i++] = Float8GetDatumFast(tmp.time[kind].total_time); + values[i++] = Float8GetDatumFast(tmp.time[kind].min_time); + values[i++] = Float8GetDatumFast(tmp.time[kind].max_time); + values[i++] = Float8GetDatumFast(tmp.time[kind].mean_time); + if (tmp.calls[kind].calls > 1) + stddev = sqrt(tmp.time[kind].sum_var_time / tmp.calls[kind].calls); + else + stddev = 0.0; + values[i++] = Float8GetDatumFast(stddev); + values[i++] = Int64GetDatumFast(tmp.calls[kind].rows); + } values[i++] = Int64GetDatumFast(tmp.blocks.shared_blks_hit); values[i++] = Int64GetDatumFast(tmp.blocks.shared_blks_read); values[i++] = Int64GetDatumFast(tmp.blocks.shared_blks_dirtied); @@ -1332,7 +1377,7 @@ entry_alloc(pgssSharedState *pgss, pgssHashKey *key, Size query_offset, int quer /* reset the statistics */ memset(&entry->counters, 0, sizeof(Counters)); /* set the appropriate initial usage count */ - entry->counters.calls.usage = sticky ? pgss->cur_median_usage : USAGE_INIT; + 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 */ @@ -2618,6 +2663,7 @@ static PlannedStmt * pgss_planner_hook(Query *parse, const char *query_string, i static PlannedStmt *pgss_planner_hook(Query *parse, int opt, ParamListInfo param) #endif { + PlannedStmt *result; if (MyProc) { int i = MyProc - ProcGlobal->allProcs; @@ -2625,14 +2671,75 @@ static PlannedStmt *pgss_planner_hook(Query *parse, int opt, ParamListInfo param pgssWaitEventEntries[i]->key.queryid = parse->queryId; } #if PG_VERSION_NUM >= 130000 - if (planner_hook_next) - return planner_hook_next(parse, query_string, cursorOptions, boundParams); - return standard_planner(parse, query_string, cursorOptions, boundParams); + if (PGSM_TRACK_PLANNING && query_string + && parse->queryId != UINT64CONST(0)) + { + instr_time start; + instr_time duration; + BufferUsage bufusage_start, + bufusage; + WalUsage walusage_start, + walusage; + + /* We need to track buffer usage as the planner can access them. */ + bufusage_start = pgBufferUsage; + + /* + * Similarly the planner could write some WAL records in some cases + * (e.g. setting a hint bit with those being WAL-logged) + */ + walusage_start = pgWalUsage; + INSTR_TIME_SET_CURRENT(start); + + plan_nested_level++; + PG_TRY(); + { + if (planner_hook_next) + result = planner_hook_next(parse, query_string, cursorOptions, boundParams); + result = standard_planner(parse, query_string, cursorOptions, boundParams); + } + PG_FINALLY(); + { + plan_nested_level--; + } + PG_END_TRY(); + + INSTR_TIME_SET_CURRENT(duration); + INSTR_TIME_SUBTRACT(duration, start); + + /* calc differences of buffer counters. */ + memset(&bufusage, 0, sizeof(BufferUsage)); + BufferUsageAccumDiff(&bufusage, &pgBufferUsage, &bufusage_start); + + /* calc differences of WAL counters. */ + memset(&walusage, 0, sizeof(WalUsage)); + WalUsageAccumDiff(&walusage, &pgWalUsage, &walusage_start); + pgss_store(query_string, + parse->queryId, + parse->stmt_location, + parse->stmt_len, + PGSS_PLAN, + INSTR_TIME_GET_MILLISEC(duration), + 0, + &bufusage, + &walusage, + NULL, + 0, + 0); + } + else + { + + if (planner_hook_next) + result = planner_hook_next(parse, query_string, cursorOptions, boundParams); + result = standard_planner(parse, query_string, cursorOptions, boundParams); + } #else if (planner_hook_next) - return planner_hook_next(parse, opt, param); - return standard_planner(parse, opt, param); + result = planner_hook_next(parse, opt, param); + result = standard_planner(parse, opt, param); #endif + return result; } static void diff --git a/pg_stat_monitor.h b/pg_stat_monitor.h index ef160bf..85f1fd7 100644 --- a/pg_stat_monitor.h +++ b/pg_stat_monitor.h @@ -36,7 +36,7 @@ #include "utils/lsyscache.h" #include "utils/guc.h" -#define IsHashInitialize() (!pgss || !pgss_hash || !pgss_object_hash || !pgss_agghash || !pgss_buckethash || !pgss_waiteventshash) +#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) @@ -73,6 +73,21 @@ typedef struct GucVariables int guc_max; bool guc_restart; } GucVariable; +typedef enum pgssStoreKind +{ + PGSS_INVALID = -1, + + /* + * PGSS_PLAN and PGSS_EXEC must be respectively 0 and 1 as they're used to + * reference the underlying values in the arrays in the Counters struct, + * and this order is required in pg_stat_statements_internal(). + */ + PGSS_PLAN = 0, + PGSS_EXEC, + + PGSS_NUMKIND /* Must be last value of this enum */ +} pgssStoreKind; + /* * Type of aggregate keys @@ -217,9 +232,9 @@ typedef struct SysInfo typedef struct Counters { uint64 bucket_id; /* bucket id */ - Calls calls; + Calls calls[PGSS_NUMKIND]; QueryInfo info; - CallTime time; + CallTime time[PGSS_NUMKIND]; Blocks blocks; SysInfo sysinfo; } Counters; @@ -344,6 +359,7 @@ void init_guc(void); #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_TRACK_PLANNING conf[11].guc_variable -GucVariable conf[11]; +GucVariable conf[12]; #endif