Make use of in-core query id added by commit 5fd9dfa5f5

Use the in-core query id computation for pg_stat_activity,
log_line_prefix, and EXPLAIN VERBOSE.

Similar to other fields in pg_stat_activity, only the queryid from the
top level statements are exposed, and if the backends status isn't
active then the queryid from the last executed statements is displayed.

Add a %Q placeholder to include the queryid in log_line_prefix, which
will also only expose top level statements.

For EXPLAIN VERBOSE, if a query identifier has been computed, either by
enabling compute_query_id or using a third-party module, display it.

Bump catalog version.

Discussion: https://postgr.es/m/20210407125726.tkvjdbw76hxnpwfi@nol

Author: Julien Rouhaud

Reviewed-by: Alvaro Herrera, Nitin Jadhav, Zhihong Yu
This commit is contained in:
Bruce Momjian 2021-04-07 14:03:56 -04:00
parent ec7ffb8096
commit 4f0b0966c8
21 changed files with 251 additions and 106 deletions

View File

@ -67,6 +67,7 @@
#include "tcop/utility.h" #include "tcop/utility.h"
#include "utils/acl.h" #include "utils/acl.h"
#include "utils/builtins.h" #include "utils/builtins.h"
#include "utils/queryjumble.h"
#include "utils/memutils.h" #include "utils/memutils.h"
#include "utils/timestamp.h" #include "utils/timestamp.h"
@ -101,6 +102,14 @@ static const uint32 PGSS_PG_MAJOR_VERSION = PG_VERSION_NUM / 100;
#define USAGE_DEALLOC_PERCENT 5 /* free this % of entries at once */ #define USAGE_DEALLOC_PERCENT 5 /* free this % of entries at once */
#define IS_STICKY(c) ((c.calls[PGSS_PLAN] + c.calls[PGSS_EXEC]) == 0) #define IS_STICKY(c) ((c.calls[PGSS_PLAN] + c.calls[PGSS_EXEC]) == 0)
/*
* Utility statements that pgss_ProcessUtility and pgss_post_parse_analyze
* ignores.
*/
#define PGSS_HANDLED_UTILITY(n) (!IsA(n, ExecuteStmt) && \
!IsA(n, PrepareStmt) && \
!IsA(n, DeallocateStmt))
/* /*
* Extension version number, for supporting older extension versions' objects * Extension version number, for supporting older extension versions' objects
*/ */
@ -309,7 +318,6 @@ static void pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
ProcessUtilityContext context, ParamListInfo params, ProcessUtilityContext context, ParamListInfo params,
QueryEnvironment *queryEnv, QueryEnvironment *queryEnv,
DestReceiver *dest, QueryCompletion *qc); DestReceiver *dest, QueryCompletion *qc);
static uint64 pgss_hash_string(const char *str, int len);
static void pgss_store(const char *query, uint64 queryId, static void pgss_store(const char *query, uint64 queryId,
int query_location, int query_len, int query_location, int query_len,
pgssStoreKind kind, pgssStoreKind kind,
@ -806,16 +814,14 @@ pgss_post_parse_analyze(ParseState *pstate, Query *query, JumbleState *jstate)
return; return;
/* /*
* Utility statements get queryId zero. We do this even in cases where * Clear queryId for prepared statements related utility, as those will
* the statement contains an optimizable statement for which a queryId * inherit from the underlying statement's one (except DEALLOCATE which is
* could be derived (such as EXPLAIN or DECLARE CURSOR). For such cases, * entirely untracked).
* runtime control will first go through ProcessUtility and then the
* executor, and we don't want the executor hooks to do anything, since we
* are already measuring the statement's costs at the utility level.
*/ */
if (query->utilityStmt) if (query->utilityStmt)
{ {
query->queryId = UINT64CONST(0); if (pgss_track_utility && !PGSS_HANDLED_UTILITY(query->utilityStmt))
query->queryId = UINT64CONST(0);
return; return;
} }
@ -1057,6 +1063,23 @@ pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
DestReceiver *dest, QueryCompletion *qc) DestReceiver *dest, QueryCompletion *qc)
{ {
Node *parsetree = pstmt->utilityStmt; Node *parsetree = pstmt->utilityStmt;
uint64 saved_queryId = pstmt->queryId;
/*
* Force utility statements to get queryId zero. We do this even in cases
* where the statement contains an optimizable statement for which a
* queryId could be derived (such as EXPLAIN or DECLARE CURSOR). For such
* cases, runtime control will first go through ProcessUtility and then the
* executor, and we don't want the executor hooks to do anything, since we
* are already measuring the statement's costs at the utility level.
*
* Note that this is only done if pg_stat_statements is enabled and
* configured to track utility statements, in the unlikely possibility
* that user configured another extension to handle utility statements
* only.
*/
if (pgss_enabled(exec_nested_level) && pgss_track_utility)
pstmt->queryId = UINT64CONST(0);
/* /*
* If it's an EXECUTE statement, we don't track it and don't increment the * If it's an EXECUTE statement, we don't track it and don't increment the
@ -1073,9 +1096,7 @@ pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
* Likewise, we don't track execution of DEALLOCATE. * Likewise, we don't track execution of DEALLOCATE.
*/ */
if (pgss_track_utility && pgss_enabled(exec_nested_level) && if (pgss_track_utility && pgss_enabled(exec_nested_level) &&
!IsA(parsetree, ExecuteStmt) && PGSS_HANDLED_UTILITY(parsetree))
!IsA(parsetree, PrepareStmt) &&
!IsA(parsetree, DeallocateStmt))
{ {
instr_time start; instr_time start;
instr_time duration; instr_time duration;
@ -1130,7 +1151,7 @@ pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
WalUsageAccumDiff(&walusage, &pgWalUsage, &walusage_start); WalUsageAccumDiff(&walusage, &pgWalUsage, &walusage_start);
pgss_store(queryString, pgss_store(queryString,
0, /* signal that it's a utility stmt */ saved_queryId,
pstmt->stmt_location, pstmt->stmt_location,
pstmt->stmt_len, pstmt->stmt_len,
PGSS_EXEC, PGSS_EXEC,
@ -1153,23 +1174,12 @@ pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
} }
} }
/*
* Given an arbitrarily long query string, produce a hash for the purposes of
* identifying the query, without normalizing constants. Used when hashing
* utility statements.
*/
static uint64
pgss_hash_string(const char *str, int len)
{
return DatumGetUInt64(hash_any_extended((const unsigned char *) str,
len, 0));
}
/* /*
* Store some statistics for a statement. * Store some statistics for a statement.
* *
* If queryId is 0 then this is a utility statement and we should compute * If queryId is 0 then this is a utility statement for which we couldn't
* a suitable queryId internally. * compute a queryId during parse analysis, and we should compute a suitable
* queryId internally.
* *
* If jstate is not NULL then we're trying to create an entry for which * If jstate is not NULL then we're trying to create an entry for which
* we have no statistics as yet; we just want to record the normalized * we have no statistics as yet; we just want to record the normalized
@ -1200,52 +1210,18 @@ pgss_store(const char *query, uint64 queryId,
return; return;
/* /*
* Confine our attention to the relevant part of the string, if the query * Nothing to do if compute_query_id isn't enabled and no other module
* is a portion of a multi-statement source string. * computed a query identifier.
*
* First apply starting offset, unless it's -1 (unknown).
*/
if (query_location >= 0)
{
Assert(query_location <= strlen(query));
query += query_location;
/* Length of 0 (or -1) means "rest of string" */
if (query_len <= 0)
query_len = strlen(query);
else
Assert(query_len <= strlen(query));
}
else
{
/* If query location is unknown, distrust query_len as well */
query_location = 0;
query_len = strlen(query);
}
/*
* Discard leading and trailing whitespace, too. Use scanner_isspace()
* not libc's isspace(), because we want to match the lexer's behavior.
*/
while (query_len > 0 && scanner_isspace(query[0]))
query++, query_location++, query_len--;
while (query_len > 0 && scanner_isspace(query[query_len - 1]))
query_len--;
/*
* For utility statements, we just hash the query string to get an ID.
*/ */
if (queryId == UINT64CONST(0)) if (queryId == UINT64CONST(0))
{ return;
queryId = pgss_hash_string(query, query_len);
/* /*
* If we are unlucky enough to get a hash of zero(invalid), use * Confine our attention to the relevant part of the string, if the query
* queryID as 2 instead, queryID 1 is already in use for normal * is a portion of a multi-statement source string, and update query
* statements. * location and length if needed.
*/ */
if (queryId == UINT64CONST(0)) query = CleanQuerytext(query, &query_location, &query_len);
queryId = UINT64CONST(2);
}
/* Set up key for hashtable search */ /* Set up key for hashtable search */
key.userid = GetUserId(); key.userid = GetUserId();

View File

@ -7004,6 +7004,15 @@ local0.* /var/log/postgresql
session processes</entry> session processes</entry>
<entry>no</entry> <entry>no</entry>
</row> </row>
<row>
<entry><literal>%Q</literal></entry>
<entry>query identifier of the current query. Query
identifiers are not computed by default, so this field
will be zero unless <xref linkend="guc-compute-query-id"/>
parameter is enabled or a third-party module that computes
query identifiers is configured.</entry>
<entry>yes</entry>
</row>
<row> <row>
<entry><literal>%%</literal></entry> <entry><literal>%%</literal></entry>
<entry>Literal <literal>%</literal></entry> <entry>Literal <literal>%</literal></entry>
@ -7480,8 +7489,8 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
<listitem> <listitem>
<para> <para>
Enables the collection of information on the currently Enables the collection of information on the currently
executing command of each session, along with the time when executing command of each session, along with its identifier and the
that command began execution. This parameter is on by time when that command began execution. This parameter is on by
default. Note that even when enabled, this information is not default. Note that even when enabled, this information is not
visible to all users, only to superusers and the user owning visible to all users, only to superusers and the user owning
the session being reported on, so it should not represent a the session being reported on, so it should not represent a
@ -7630,12 +7639,16 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
</term> </term>
<listitem> <listitem>
<para> <para>
Enables in-core computation of a query identifier. The <xref Enables in-core computation of a query identifier.
linkend="pgstatstatements"/> extension requires a query identifier Query identifiers can be displayed in the <link
to be computed. Note that an external module can alternatively linkend="monitoring-pg-stat-activity-view"><structname>pg_stat_activity</structname></link>
be used if the in-core query identifier computation method view, using <command>EXPLAIN</command>, or emitted in the log if
isn't acceptable. In this case, in-core computation should configured via the <xref linkend="guc-log-line-prefix"/> parameter.
remain disabled. The default is <literal>off</literal>. The <xref linkend="pgstatstatements"/> extension also requires a query
identifier to be computed. Note that an external module can
alternatively be used if the in-core query identifier computation
specification isn't acceptable. In this case, in-core computation
must be disabled. The default is <literal>off</literal>.
</para> </para>
<note> <note>
<para> <para>

View File

@ -910,6 +910,22 @@ postgres 27093 0.0 0.0 30096 2752 ? Ss 11:34 0:00 postgres: ser
</para></entry> </para></entry>
</row> </row>
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>queryid</structfield> <type>bigint</type>
</para>
<para>
Identifier of this backend's most recent query. If
<structfield>state</structfield> is <literal>active</literal> this
field shows the identifier of the currently executing query. In
all other states, it shows the identifier of last query that was
executed. Query identifiers are not computed by default so this
field will be null unless <xref linkend="guc-compute-query-id"/>
parameter is enabled or a third-party module that computes query
identifiers is configured.
</para></entry>
</row>
<row> <row>
<entry role="catalog_table_entry"><para role="column_definition"> <entry role="catalog_table_entry"><para role="column_definition">
<structfield>query</structfield> <type>text</type> <structfield>query</structfield> <type>text</type>

View File

@ -136,8 +136,10 @@ ROLLBACK;
the output column list for each node in the plan tree, schema-qualify the output column list for each node in the plan tree, schema-qualify
table and function names, always label variables in expressions with table and function names, always label variables in expressions with
their range table alias, and always print the name of each trigger for their range table alias, and always print the name of each trigger for
which statistics are displayed. This parameter defaults to which statistics are displayed. The query identifier will also be
<literal>FALSE</literal>. displayed if one has been computed, see <xref
linkend="guc-compute-query-id"/> for more details. This parameter
defaults to <literal>FALSE</literal>.
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>

View File

@ -833,6 +833,7 @@ CREATE VIEW pg_stat_activity AS
S.state, S.state,
S.backend_xid, S.backend_xid,
s.backend_xmin, s.backend_xmin,
S.queryid,
S.query, S.query,
S.backend_type S.backend_type
FROM pg_stat_get_activity(NULL) AS S FROM pg_stat_get_activity(NULL) AS S

View File

@ -24,6 +24,7 @@
#include "nodes/extensible.h" #include "nodes/extensible.h"
#include "nodes/makefuncs.h" #include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h" #include "nodes/nodeFuncs.h"
#include "parser/analyze.h"
#include "parser/parsetree.h" #include "parser/parsetree.h"
#include "rewrite/rewriteHandler.h" #include "rewrite/rewriteHandler.h"
#include "storage/bufmgr.h" #include "storage/bufmgr.h"
@ -165,6 +166,8 @@ ExplainQuery(ParseState *pstate, ExplainStmt *stmt,
{ {
ExplainState *es = NewExplainState(); ExplainState *es = NewExplainState();
TupOutputState *tstate; TupOutputState *tstate;
JumbleState *jstate = NULL;
Query *query;
List *rewritten; List *rewritten;
ListCell *lc; ListCell *lc;
bool timing_set = false; bool timing_set = false;
@ -241,6 +244,13 @@ ExplainQuery(ParseState *pstate, ExplainStmt *stmt,
/* if the summary was not set explicitly, set default value */ /* if the summary was not set explicitly, set default value */
es->summary = (summary_set) ? es->summary : es->analyze; es->summary = (summary_set) ? es->summary : es->analyze;
query = castNode(Query, stmt->query);
if (compute_query_id)
jstate = JumbleQuery(query, pstate->p_sourcetext);
if (post_parse_analyze_hook)
(*post_parse_analyze_hook) (pstate, query, jstate);
/* /*
* Parse analysis was done already, but we still have to run the rule * Parse analysis was done already, but we still have to run the rule
* rewriter. We do not do AcquireRewriteLocks: we assume the query either * rewriter. We do not do AcquireRewriteLocks: we assume the query either
@ -600,6 +610,14 @@ ExplainOnePlan(PlannedStmt *plannedstmt, IntoClause *into, ExplainState *es,
/* Create textual dump of plan tree */ /* Create textual dump of plan tree */
ExplainPrintPlan(es, queryDesc); ExplainPrintPlan(es, queryDesc);
if (es->verbose && plannedstmt->queryId != UINT64CONST(0))
{
char buf[MAXINT8LEN+1];
pg_lltoa(plannedstmt->queryId, buf);
ExplainPropertyText("Query Identifier", buf, es);
}
/* Show buffer usage in planning */ /* Show buffer usage in planning */
if (bufusage) if (bufusage)
{ {

View File

@ -58,6 +58,7 @@
#include "storage/lmgr.h" #include "storage/lmgr.h"
#include "tcop/utility.h" #include "tcop/utility.h"
#include "utils/acl.h" #include "utils/acl.h"
#include "utils/backend_status.h"
#include "utils/lsyscache.h" #include "utils/lsyscache.h"
#include "utils/memutils.h" #include "utils/memutils.h"
#include "utils/partcache.h" #include "utils/partcache.h"
@ -128,6 +129,14 @@ static void EvalPlanQualStart(EPQState *epqstate, Plan *planTree);
void void
ExecutorStart(QueryDesc *queryDesc, int eflags) ExecutorStart(QueryDesc *queryDesc, int eflags)
{ {
/*
* In some cases (e.g. an EXECUTE statement) a query execution will skip
* parse analysis, which means that the queryid won't be reported. Note
* that it's harmless to report the queryid multiple time, as the call will
* be ignored if the top level queryid has already been reported.
*/
pgstat_report_queryid(queryDesc->plannedstmt->queryId, false);
if (ExecutorStart_hook) if (ExecutorStart_hook)
(*ExecutorStart_hook) (queryDesc, eflags); (*ExecutorStart_hook) (queryDesc, eflags);
else else

View File

@ -175,7 +175,7 @@ ExecSerializePlan(Plan *plan, EState *estate)
*/ */
pstmt = makeNode(PlannedStmt); pstmt = makeNode(PlannedStmt);
pstmt->commandType = CMD_SELECT; pstmt->commandType = CMD_SELECT;
pstmt->queryId = UINT64CONST(0); pstmt->queryId = pgstat_get_my_queryid();
pstmt->hasReturning = false; pstmt->hasReturning = false;
pstmt->hasModifyingCTE = false; pstmt->hasModifyingCTE = false;
pstmt->canSetTag = true; pstmt->canSetTag = true;
@ -1421,8 +1421,9 @@ ParallelQueryMain(dsm_segment *seg, shm_toc *toc)
/* Setting debug_query_string for individual workers */ /* Setting debug_query_string for individual workers */
debug_query_string = queryDesc->sourceText; debug_query_string = queryDesc->sourceText;
/* Report workers' query for monitoring purposes */ /* Report workers' query and queryId for monitoring purposes */
pgstat_report_activity(STATE_RUNNING, debug_query_string); pgstat_report_activity(STATE_RUNNING, debug_query_string);
pgstat_report_queryid(queryDesc->plannedstmt->queryId, false);
/* Attach to the dynamic shared memory area. */ /* Attach to the dynamic shared memory area. */
area_space = shm_toc_lookup(toc, PARALLEL_KEY_DSA, false); area_space = shm_toc_lookup(toc, PARALLEL_KEY_DSA, false);

View File

@ -45,6 +45,7 @@
#include "parser/parse_type.h" #include "parser/parse_type.h"
#include "parser/parsetree.h" #include "parser/parsetree.h"
#include "rewrite/rewriteManip.h" #include "rewrite/rewriteManip.h"
#include "utils/backend_status.h"
#include "utils/builtins.h" #include "utils/builtins.h"
#include "utils/guc.h" #include "utils/guc.h"
#include "utils/queryjumble.h" #include "utils/queryjumble.h"
@ -130,6 +131,8 @@ parse_analyze(RawStmt *parseTree, const char *sourceText,
free_parsestate(pstate); free_parsestate(pstate);
pgstat_report_queryid(query->queryId, false);
return query; return query;
} }
@ -167,6 +170,8 @@ parse_analyze_varparams(RawStmt *parseTree, const char *sourceText,
free_parsestate(pstate); free_parsestate(pstate);
pgstat_report_queryid(query->queryId, false);
return query; return query;
} }

View File

@ -695,6 +695,8 @@ pg_analyze_and_rewrite_params(RawStmt *parsetree,
free_parsestate(pstate); free_parsestate(pstate);
pgstat_report_queryid(query->queryId, false);
if (log_parser_stats) if (log_parser_stats)
ShowUsage("PARSE ANALYSIS STATISTICS"); ShowUsage("PARSE ANALYSIS STATISTICS");
@ -913,6 +915,7 @@ pg_plan_queries(List *querytrees, const char *query_string, int cursorOptions,
stmt->utilityStmt = query->utilityStmt; stmt->utilityStmt = query->utilityStmt;
stmt->stmt_location = query->stmt_location; stmt->stmt_location = query->stmt_location;
stmt->stmt_len = query->stmt_len; stmt->stmt_len = query->stmt_len;
stmt->queryId = query->queryId;
} }
else else
{ {
@ -1029,6 +1032,8 @@ exec_simple_query(const char *query_string)
DestReceiver *receiver; DestReceiver *receiver;
int16 format; int16 format;
pgstat_report_queryid(0, true);
/* /*
* Get the command name for use in status display (it also becomes the * Get the command name for use in status display (it also becomes the
* default completion tag, down inside PortalRun). Set ps_status and * default completion tag, down inside PortalRun). Set ps_status and

View File

@ -544,6 +544,7 @@ pgstat_report_activity(BackendState state, const char *cmd_str)
beentry->st_activity_start_timestamp = 0; beentry->st_activity_start_timestamp = 0;
/* st_xact_start_timestamp and wait_event_info are also disabled */ /* st_xact_start_timestamp and wait_event_info are also disabled */
beentry->st_xact_start_timestamp = 0; beentry->st_xact_start_timestamp = 0;
beentry->st_queryid = UINT64CONST(0);
proc->wait_event_info = 0; proc->wait_event_info = 0;
PGSTAT_END_WRITE_ACTIVITY(beentry); PGSTAT_END_WRITE_ACTIVITY(beentry);
} }
@ -598,6 +599,14 @@ pgstat_report_activity(BackendState state, const char *cmd_str)
beentry->st_state = state; beentry->st_state = state;
beentry->st_state_start_timestamp = current_timestamp; beentry->st_state_start_timestamp = current_timestamp;
/*
* If a new query is started, we reset the query identifier as it'll only
* be known after parse analysis, to avoid reporting last query's
* identifier.
*/
if (state == STATE_RUNNING)
beentry->st_queryid = UINT64CONST(0);
if (cmd_str != NULL) if (cmd_str != NULL)
{ {
memcpy((char *) beentry->st_activity_raw, cmd_str, len); memcpy((char *) beentry->st_activity_raw, cmd_str, len);
@ -608,6 +617,46 @@ pgstat_report_activity(BackendState state, const char *cmd_str)
PGSTAT_END_WRITE_ACTIVITY(beentry); PGSTAT_END_WRITE_ACTIVITY(beentry);
} }
/* --------
* pgstat_report_queryid() -
*
* Called to update top-level query identifier.
* --------
*/
void
pgstat_report_queryid(uint64 queryId, bool force)
{
volatile PgBackendStatus *beentry = MyBEEntry;
/*
* if track_activities is disabled, st_queryid should already have been
* reset
*/
if (!beentry || !pgstat_track_activities)
return;
/*
* We only report the top-level query identifiers. The stored queryid is
* reset when a backend calls pgstat_report_activity(STATE_RUNNING), or
* with an explicit call to this function using the force flag. If the
* saved query identifier is not zero it means that it's not a top-level
* command, so ignore the one provided unless it's an explicit call to
* reset the identifier.
*/
if (beentry->st_queryid != 0 && !force)
return;
/*
* Update my status entry, following the protocol of bumping
* st_changecount before and after. We use a volatile pointer here to
* ensure the compiler doesn't try to get cute.
*/
PGSTAT_BEGIN_WRITE_ACTIVITY(beentry);
beentry->st_queryid = queryId;
PGSTAT_END_WRITE_ACTIVITY(beentry);
}
/* ---------- /* ----------
* pgstat_report_appname() - * pgstat_report_appname() -
* *
@ -972,6 +1021,25 @@ pgstat_get_crashed_backend_activity(int pid, char *buffer, int buflen)
return NULL; return NULL;
} }
/* ----------
* pgstat_get_my_queryid() -
*
* Return current backend's query identifier.
*/
uint64
pgstat_get_my_queryid(void)
{
if (!MyBEEntry)
return 0;
/* There's no need for a look around pgstat_begin_read_activity /
* pgstat_end_read_activity here as it's only called from
* pg_stat_get_activity which is already protected, or from the same
* backend which mean that there won't be concurrent write.
*/
return MyBEEntry->st_queryid;
}
/* ---------- /* ----------
* pgstat_fetch_stat_beentry() - * pgstat_fetch_stat_beentry() -

View File

@ -569,7 +569,7 @@ pg_stat_get_progress_info(PG_FUNCTION_ARGS)
Datum Datum
pg_stat_get_activity(PG_FUNCTION_ARGS) pg_stat_get_activity(PG_FUNCTION_ARGS)
{ {
#define PG_STAT_GET_ACTIVITY_COLS 29 #define PG_STAT_GET_ACTIVITY_COLS 30
int num_backends = pgstat_fetch_stat_numbackends(); int num_backends = pgstat_fetch_stat_numbackends();
int curr_backend; int curr_backend;
int pid = PG_ARGISNULL(0) ? -1 : PG_GETARG_INT32(0); int pid = PG_ARGISNULL(0) ? -1 : PG_GETARG_INT32(0);
@ -914,6 +914,10 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
values[27] = BoolGetDatum(false); /* GSS Encryption not in values[27] = BoolGetDatum(false); /* GSS Encryption not in
* use */ * use */
} }
if (beentry->st_queryid == 0)
nulls[29] = true;
else
values[29] = DatumGetUInt64(beentry->st_queryid);
} }
else else
{ {
@ -941,6 +945,7 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
nulls[26] = true; nulls[26] = true;
nulls[27] = true; nulls[27] = true;
nulls[28] = true; nulls[28] = true;
nulls[29] = true;
} }
tuplestore_putvalues(tupstore, tupdesc, values, nulls); tuplestore_putvalues(tupstore, tupdesc, values, nulls);

View File

@ -2714,6 +2714,14 @@ log_line_prefix(StringInfo buf, ErrorData *edata)
else else
appendStringInfoString(buf, unpack_sql_state(edata->sqlerrcode)); appendStringInfoString(buf, unpack_sql_state(edata->sqlerrcode));
break; break;
case 'Q':
if (padding != 0)
appendStringInfo(buf, "%*ld", padding,
pgstat_get_my_queryid());
else
appendStringInfo(buf, "%ld",
pgstat_get_my_queryid());
break;
default: default:
/* format error - ignore it */ /* format error - ignore it */
break; break;

View File

@ -543,6 +543,7 @@
# %t = timestamp without milliseconds # %t = timestamp without milliseconds
# %m = timestamp with milliseconds # %m = timestamp with milliseconds
# %n = timestamp with milliseconds (as a Unix epoch) # %n = timestamp with milliseconds (as a Unix epoch)
# %Q = query ID (0 if none or not computed)
# %i = command tag # %i = command tag
# %e = SQL state # %e = SQL state
# %c = session ID # %c = session ID

View File

@ -39,7 +39,7 @@
#define JUMBLE_SIZE 1024 /* query serialization buffer size */ #define JUMBLE_SIZE 1024 /* query serialization buffer size */
static uint64 compute_utility_queryid(const char *str, int query_len); static uint64 compute_utility_queryid(const char *str, int query_location, int query_len);
static void AppendJumble(JumbleState *jstate, static void AppendJumble(JumbleState *jstate,
const unsigned char *item, Size size); const unsigned char *item, Size size);
static void JumbleQueryInternal(JumbleState *jstate, Query *query); static void JumbleQueryInternal(JumbleState *jstate, Query *query);
@ -97,17 +97,9 @@ JumbleQuery(Query *query, const char *querytext)
JumbleState *jstate = NULL; JumbleState *jstate = NULL;
if (query->utilityStmt) if (query->utilityStmt)
{ {
const char *sql; query->queryId = compute_utility_queryid(querytext,
int query_location = query->stmt_location; query->stmt_location,
int query_len = query->stmt_len; query->stmt_len);
/*
* Confine our attention to the relevant part of the string, if the
* query is a portion of a multi-statement source string.
*/
sql = CleanQuerytext(querytext, &query_location, &query_len);
query->queryId = compute_utility_queryid(sql, query_len);
} }
else else
{ {
@ -143,11 +135,18 @@ JumbleQuery(Query *query, const char *querytext)
* Compute a query identifier for the given utility query string. * Compute a query identifier for the given utility query string.
*/ */
static uint64 static uint64
compute_utility_queryid(const char *str, int query_len) compute_utility_queryid(const char *query_text, int query_location, int query_len)
{ {
uint64 queryId; uint64 queryId;
const char *sql;
queryId = DatumGetUInt64(hash_any_extended((const unsigned char *) str, /*
* Confine our attention to the relevant part of the string, if the
* query is a portion of a multi-statement source string.
*/
sql = CleanQuerytext(query_text, &query_location, &query_len);
queryId = DatumGetUInt64(hash_any_extended((const unsigned char *) sql,
query_len, 0)); query_len, 0));
/* /*

View File

@ -53,6 +53,6 @@
*/ */
/* yyyymmddN */ /* yyyymmddN */
#define CATALOG_VERSION_NO 202104062 #define CATALOG_VERSION_NO 202104071
#endif #endif

View File

@ -5278,9 +5278,9 @@
proname => 'pg_stat_get_activity', prorows => '100', proisstrict => 'f', proname => 'pg_stat_get_activity', prorows => '100', proisstrict => 'f',
proretset => 't', provolatile => 's', proparallel => 'r', proretset => 't', provolatile => 's', proparallel => 'r',
prorettype => 'record', proargtypes => 'int4', prorettype => 'record', proargtypes => 'int4',
proallargtypes => '{int4,oid,int4,oid,text,text,text,text,text,timestamptz,timestamptz,timestamptz,timestamptz,inet,text,int4,xid,xid,text,bool,text,text,int4,text,numeric,text,bool,text,bool,int4}', proallargtypes => '{int4,oid,int4,oid,text,text,text,text,text,timestamptz,timestamptz,timestamptz,timestamptz,inet,text,int4,xid,xid,text,bool,text,text,int4,text,numeric,text,bool,text,bool,int4,int8}',
proargmodes => '{i,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o}', proargmodes => '{i,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o}',
proargnames => '{pid,datid,pid,usesysid,application_name,state,query,wait_event_type,wait_event,xact_start,query_start,backend_start,state_change,client_addr,client_hostname,client_port,backend_xid,backend_xmin,backend_type,ssl,sslversion,sslcipher,sslbits,ssl_client_dn,ssl_client_serial,ssl_issuer_dn,gss_auth,gss_princ,gss_enc,leader_pid}', proargnames => '{pid,datid,pid,usesysid,application_name,state,query,wait_event_type,wait_event,xact_start,query_start,backend_start,state_change,client_addr,client_hostname,client_port,backend_xid,backend_xmin,backend_type,ssl,sslversion,sslcipher,sslbits,ssl_client_dn,ssl_client_serial,ssl_issuer_dn,gss_auth,gss_princ,gss_enc,leader_pid,queryid}',
prosrc => 'pg_stat_get_activity' }, prosrc => 'pg_stat_get_activity' },
{ oid => '3318', { oid => '3318',
descr => 'statistics: information about progress of backends running maintenance command', descr => 'statistics: information about progress of backends running maintenance command',

View File

@ -165,6 +165,9 @@ typedef struct PgBackendStatus
ProgressCommandType st_progress_command; ProgressCommandType st_progress_command;
Oid st_progress_command_target; Oid st_progress_command_target;
int64 st_progress_param[PGSTAT_NUM_PROGRESS_PARAM]; int64 st_progress_param[PGSTAT_NUM_PROGRESS_PARAM];
/* query identifier, optionally computed using post_parse_analyze_hook */
uint64 st_queryid;
} PgBackendStatus; } PgBackendStatus;
@ -294,12 +297,14 @@ extern void pgstat_clear_backend_activity_snapshot(void);
/* Activity reporting functions */ /* Activity reporting functions */
extern void pgstat_report_activity(BackendState state, const char *cmd_str); extern void pgstat_report_activity(BackendState state, const char *cmd_str);
extern void pgstat_report_queryid(uint64 queryId, bool force);
extern void pgstat_report_tempfile(size_t filesize); extern void pgstat_report_tempfile(size_t filesize);
extern void pgstat_report_appname(const char *appname); extern void pgstat_report_appname(const char *appname);
extern void pgstat_report_xact_timestamp(TimestampTz tstamp); extern void pgstat_report_xact_timestamp(TimestampTz tstamp);
extern const char *pgstat_get_backend_current_activity(int pid, bool checkUser); extern const char *pgstat_get_backend_current_activity(int pid, bool checkUser);
extern const char *pgstat_get_crashed_backend_activity(int pid, char *buffer, extern const char *pgstat_get_crashed_backend_activity(int pid, char *buffer,
int buflen); int buflen);
extern uint64 pgstat_get_my_queryid(void);
/* ---------- /* ----------

View File

@ -17,7 +17,7 @@ begin
for ln in execute $1 for ln in execute $1
loop loop
-- Replace any numeric word with just 'N' -- Replace any numeric word with just 'N'
ln := regexp_replace(ln, '\m\d+\M', 'N', 'g'); ln := regexp_replace(ln, '-?\m\d+\M', 'N', 'g');
-- In sort output, the above won't match units-suffixed numbers -- In sort output, the above won't match units-suffixed numbers
ln := regexp_replace(ln, '\m\d+kB', 'NkB', 'g'); ln := regexp_replace(ln, '\m\d+kB', 'NkB', 'g');
-- Ignore text-mode buffers output because it varies depending -- Ignore text-mode buffers output because it varies depending
@ -477,3 +477,12 @@ select jsonb_pretty(
(1 row) (1 row)
rollback; rollback;
set compute_query_id = on;
select explain_filter('explain (verbose) select 1');
explain_filter
----------------------------------------
Result (cost=N.N..N.N rows=N width=N)
Output: N
Query Identifier: N
(3 rows)

View File

@ -1762,9 +1762,10 @@ pg_stat_activity| SELECT s.datid,
s.state, s.state,
s.backend_xid, s.backend_xid,
s.backend_xmin, s.backend_xmin,
s.queryid,
s.query, s.query,
s.backend_type s.backend_type
FROM ((pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid) FROM ((pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, queryid)
LEFT JOIN pg_database d ON ((s.datid = d.oid))) LEFT JOIN pg_database d ON ((s.datid = d.oid)))
LEFT JOIN pg_authid u ON ((s.usesysid = u.oid))); LEFT JOIN pg_authid u ON ((s.usesysid = u.oid)));
pg_stat_all_indexes| SELECT c.oid AS relid, pg_stat_all_indexes| SELECT c.oid AS relid,
@ -1876,7 +1877,7 @@ pg_stat_gssapi| SELECT s.pid,
s.gss_auth AS gss_authenticated, s.gss_auth AS gss_authenticated,
s.gss_princ AS principal, s.gss_princ AS principal,
s.gss_enc AS encrypted s.gss_enc AS encrypted
FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid) FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, queryid)
WHERE (s.client_port IS NOT NULL); WHERE (s.client_port IS NOT NULL);
pg_stat_progress_analyze| SELECT s.pid, pg_stat_progress_analyze| SELECT s.pid,
s.datid, s.datid,
@ -2046,7 +2047,7 @@ pg_stat_replication| SELECT s.pid,
w.sync_priority, w.sync_priority,
w.sync_state, w.sync_state,
w.reply_time w.reply_time
FROM ((pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid) FROM ((pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, queryid)
JOIN pg_stat_get_wal_senders() w(pid, state, sent_lsn, write_lsn, flush_lsn, replay_lsn, write_lag, flush_lag, replay_lag, sync_priority, sync_state, reply_time) ON ((s.pid = w.pid))) JOIN pg_stat_get_wal_senders() w(pid, state, sent_lsn, write_lsn, flush_lsn, replay_lsn, write_lag, flush_lag, replay_lag, sync_priority, sync_state, reply_time) ON ((s.pid = w.pid)))
LEFT JOIN pg_authid u ON ((s.usesysid = u.oid))); LEFT JOIN pg_authid u ON ((s.usesysid = u.oid)));
pg_stat_replication_slots| SELECT s.slot_name, pg_stat_replication_slots| SELECT s.slot_name,
@ -2076,7 +2077,7 @@ pg_stat_ssl| SELECT s.pid,
s.ssl_client_dn AS client_dn, s.ssl_client_dn AS client_dn,
s.ssl_client_serial AS client_serial, s.ssl_client_serial AS client_serial,
s.ssl_issuer_dn AS issuer_dn s.ssl_issuer_dn AS issuer_dn
FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid) FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, queryid)
WHERE (s.client_port IS NOT NULL); WHERE (s.client_port IS NOT NULL);
pg_stat_subscription| SELECT su.oid AS subid, pg_stat_subscription| SELECT su.oid AS subid,
su.subname, su.subname,

View File

@ -19,7 +19,7 @@ begin
for ln in execute $1 for ln in execute $1
loop loop
-- Replace any numeric word with just 'N' -- Replace any numeric word with just 'N'
ln := regexp_replace(ln, '\m\d+\M', 'N', 'g'); ln := regexp_replace(ln, '-?\m\d+\M', 'N', 'g');
-- In sort output, the above won't match units-suffixed numbers -- In sort output, the above won't match units-suffixed numbers
ln := regexp_replace(ln, '\m\d+kB', 'NkB', 'g'); ln := regexp_replace(ln, '\m\d+kB', 'NkB', 'g');
-- Ignore text-mode buffers output because it varies depending -- Ignore text-mode buffers output because it varies depending
@ -103,3 +103,6 @@ select jsonb_pretty(
); );
rollback; rollback;
set compute_query_id = on;
select explain_filter('explain (verbose) select 1');