Skip to content

Commit 4f0b096

Browse files
committed
Make use of in-core query id added by commit 5fd9dfa
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
1 parent ec7ffb8 commit 4f0b096

File tree

21 files changed

+250
-105
lines changed

21 files changed

+250
-105
lines changed

contrib/pg_stat_statements/pg_stat_statements.c

+44-68
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
#include "tcop/utility.h"
6868
#include "utils/acl.h"
6969
#include "utils/builtins.h"
70+
#include "utils/queryjumble.h"
7071
#include "utils/memutils.h"
7172
#include "utils/timestamp.h"
7273

@@ -101,6 +102,14 @@ static const uint32 PGSS_PG_MAJOR_VERSION = PG_VERSION_NUM / 100;
101102
#define USAGE_DEALLOC_PERCENT 5 /* free this % of entries at once */
102103
#define IS_STICKY(c) ((c.calls[PGSS_PLAN] + c.calls[PGSS_EXEC]) == 0)
103104

105+
/*
106+
* Utility statements that pgss_ProcessUtility and pgss_post_parse_analyze
107+
* ignores.
108+
*/
109+
#define PGSS_HANDLED_UTILITY(n) (!IsA(n, ExecuteStmt) && \
110+
!IsA(n, PrepareStmt) && \
111+
!IsA(n, DeallocateStmt))
112+
104113
/*
105114
* Extension version number, for supporting older extension versions' objects
106115
*/
@@ -309,7 +318,6 @@ static void pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
309318
ProcessUtilityContext context, ParamListInfo params,
310319
QueryEnvironment *queryEnv,
311320
DestReceiver *dest, QueryCompletion *qc);
312-
static uint64 pgss_hash_string(const char *str, int len);
313321
static void pgss_store(const char *query, uint64 queryId,
314322
int query_location, int query_len,
315323
pgssStoreKind kind,
@@ -806,16 +814,14 @@ pgss_post_parse_analyze(ParseState *pstate, Query *query, JumbleState *jstate)
806814
return;
807815

808816
/*
809-
* Utility statements get queryId zero. We do this even in cases where
810-
* the statement contains an optimizable statement for which a queryId
811-
* could be derived (such as EXPLAIN or DECLARE CURSOR). For such cases,
812-
* runtime control will first go through ProcessUtility and then the
813-
* executor, and we don't want the executor hooks to do anything, since we
814-
* are already measuring the statement's costs at the utility level.
817+
* Clear queryId for prepared statements related utility, as those will
818+
* inherit from the underlying statement's one (except DEALLOCATE which is
819+
* entirely untracked).
815820
*/
816821
if (query->utilityStmt)
817822
{
818-
query->queryId = UINT64CONST(0);
823+
if (pgss_track_utility && !PGSS_HANDLED_UTILITY(query->utilityStmt))
824+
query->queryId = UINT64CONST(0);
819825
return;
820826
}
821827

@@ -1057,6 +1063,23 @@ pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
10571063
DestReceiver *dest, QueryCompletion *qc)
10581064
{
10591065
Node *parsetree = pstmt->utilityStmt;
1066+
uint64 saved_queryId = pstmt->queryId;
1067+
1068+
/*
1069+
* Force utility statements to get queryId zero. We do this even in cases
1070+
* where the statement contains an optimizable statement for which a
1071+
* queryId could be derived (such as EXPLAIN or DECLARE CURSOR). For such
1072+
* cases, runtime control will first go through ProcessUtility and then the
1073+
* executor, and we don't want the executor hooks to do anything, since we
1074+
* are already measuring the statement's costs at the utility level.
1075+
*
1076+
* Note that this is only done if pg_stat_statements is enabled and
1077+
* configured to track utility statements, in the unlikely possibility
1078+
* that user configured another extension to handle utility statements
1079+
* only.
1080+
*/
1081+
if (pgss_enabled(exec_nested_level) && pgss_track_utility)
1082+
pstmt->queryId = UINT64CONST(0);
10601083

10611084
/*
10621085
* 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,
10731096
* Likewise, we don't track execution of DEALLOCATE.
10741097
*/
10751098
if (pgss_track_utility && pgss_enabled(exec_nested_level) &&
1076-
!IsA(parsetree, ExecuteStmt) &&
1077-
!IsA(parsetree, PrepareStmt) &&
1078-
!IsA(parsetree, DeallocateStmt))
1099+
PGSS_HANDLED_UTILITY(parsetree))
10791100
{
10801101
instr_time start;
10811102
instr_time duration;
@@ -1130,7 +1151,7 @@ pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
11301151
WalUsageAccumDiff(&walusage, &pgWalUsage, &walusage_start);
11311152

11321153
pgss_store(queryString,
1133-
0, /* signal that it's a utility stmt */
1154+
saved_queryId,
11341155
pstmt->stmt_location,
11351156
pstmt->stmt_len,
11361157
PGSS_EXEC,
@@ -1153,23 +1174,12 @@ pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
11531174
}
11541175
}
11551176

1156-
/*
1157-
* Given an arbitrarily long query string, produce a hash for the purposes of
1158-
* identifying the query, without normalizing constants. Used when hashing
1159-
* utility statements.
1160-
*/
1161-
static uint64
1162-
pgss_hash_string(const char *str, int len)
1163-
{
1164-
return DatumGetUInt64(hash_any_extended((const unsigned char *) str,
1165-
len, 0));
1166-
}
1167-
11681177
/*
11691178
* Store some statistics for a statement.
11701179
*
1171-
* If queryId is 0 then this is a utility statement and we should compute
1172-
* a suitable queryId internally.
1180+
* If queryId is 0 then this is a utility statement for which we couldn't
1181+
* compute a queryId during parse analysis, and we should compute a suitable
1182+
* queryId internally.
11731183
*
11741184
* If jstate is not NULL then we're trying to create an entry for which
11751185
* we have no statistics as yet; we just want to record the normalized
@@ -1200,52 +1210,18 @@ pgss_store(const char *query, uint64 queryId,
12001210
return;
12011211

12021212
/*
1203-
* Confine our attention to the relevant part of the string, if the query
1204-
* is a portion of a multi-statement source string.
1205-
*
1206-
* First apply starting offset, unless it's -1 (unknown).
1207-
*/
1208-
if (query_location >= 0)
1209-
{
1210-
Assert(query_location <= strlen(query));
1211-
query += query_location;
1212-
/* Length of 0 (or -1) means "rest of string" */
1213-
if (query_len <= 0)
1214-
query_len = strlen(query);
1215-
else
1216-
Assert(query_len <= strlen(query));
1217-
}
1218-
else
1219-
{
1220-
/* If query location is unknown, distrust query_len as well */
1221-
query_location = 0;
1222-
query_len = strlen(query);
1223-
}
1224-
1225-
/*
1226-
* Discard leading and trailing whitespace, too. Use scanner_isspace()
1227-
* not libc's isspace(), because we want to match the lexer's behavior.
1213+
* Nothing to do if compute_query_id isn't enabled and no other module
1214+
* computed a query identifier.
12281215
*/
1229-
while (query_len > 0 && scanner_isspace(query[0]))
1230-
query++, query_location++, query_len--;
1231-
while (query_len > 0 && scanner_isspace(query[query_len - 1]))
1232-
query_len--;
1216+
if (queryId == UINT64CONST(0))
1217+
return;
12331218

12341219
/*
1235-
* For utility statements, we just hash the query string to get an ID.
1220+
* Confine our attention to the relevant part of the string, if the query
1221+
* is a portion of a multi-statement source string, and update query
1222+
* location and length if needed.
12361223
*/
1237-
if (queryId == UINT64CONST(0))
1238-
{
1239-
queryId = pgss_hash_string(query, query_len);
1240-
1241-
/*
1242-
* If we are unlucky enough to get a hash of zero(invalid), use
1243-
* queryID as 2 instead, queryID 1 is already in use for normal
1244-
* statements.
1245-
*/
1246-
if (queryId == UINT64CONST(0))
1247-
queryId = UINT64CONST(2);
1248-
}
1224+
query = CleanQuerytext(query, &query_location, &query_len);
12491225

12501226
/* Set up key for hashtable search */
12511227
key.userid = GetUserId();

doc/src/sgml/config.sgml

+21-8
Original file line numberDiff line numberDiff line change
@@ -7004,6 +7004,15 @@ local0.* /var/log/postgresql
70047004
session processes</entry>
70057005
<entry>no</entry>
70067006
</row>
7007+
<row>
7008+
<entry><literal>%Q</literal></entry>
7009+
<entry>query identifier of the current query. Query
7010+
identifiers are not computed by default, so this field
7011+
will be zero unless <xref linkend="guc-compute-query-id"/>
7012+
parameter is enabled or a third-party module that computes
7013+
query identifiers is configured.</entry>
7014+
<entry>yes</entry>
7015+
</row>
70077016
<row>
70087017
<entry><literal>%%</literal></entry>
70097018
<entry>Literal <literal>%</literal></entry>
@@ -7480,8 +7489,8 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
74807489
<listitem>
74817490
<para>
74827491
Enables the collection of information on the currently
7483-
executing command of each session, along with the time when
7484-
that command began execution. This parameter is on by
7492+
executing command of each session, along with its identifier and the
7493+
time when that command began execution. This parameter is on by
74857494
default. Note that even when enabled, this information is not
74867495
visible to all users, only to superusers and the user owning
74877496
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;
76307639
</term>
76317640
<listitem>
76327641
<para>
7633-
Enables in-core computation of a query identifier. The <xref
7634-
linkend="pgstatstatements"/> extension requires a query identifier
7635-
to be computed. Note that an external module can alternatively
7636-
be used if the in-core query identifier computation method
7637-
isn't acceptable. In this case, in-core computation should
7638-
remain disabled. The default is <literal>off</literal>.
7642+
Enables in-core computation of a query identifier.
7643+
Query identifiers can be displayed in the <link
7644+
linkend="monitoring-pg-stat-activity-view"><structname>pg_stat_activity</structname></link>
7645+
view, using <command>EXPLAIN</command>, or emitted in the log if
7646+
configured via the <xref linkend="guc-log-line-prefix"/> parameter.
7647+
The <xref linkend="pgstatstatements"/> extension also requires a query
7648+
identifier to be computed. Note that an external module can
7649+
alternatively be used if the in-core query identifier computation
7650+
specification isn't acceptable. In this case, in-core computation
7651+
must be disabled. The default is <literal>off</literal>.
76397652
</para>
76407653
<note>
76417654
<para>

doc/src/sgml/monitoring.sgml

+16
Original file line numberDiff line numberDiff line change
@@ -910,6 +910,22 @@ postgres 27093 0.0 0.0 30096 2752 ? Ss 11:34 0:00 postgres: ser
910910
</para></entry>
911911
</row>
912912

913+
<row>
914+
<entry role="catalog_table_entry"><para role="column_definition">
915+
<structfield>queryid</structfield> <type>bigint</type>
916+
</para>
917+
<para>
918+
Identifier of this backend's most recent query. If
919+
<structfield>state</structfield> is <literal>active</literal> this
920+
field shows the identifier of the currently executing query. In
921+
all other states, it shows the identifier of last query that was
922+
executed. Query identifiers are not computed by default so this
923+
field will be null unless <xref linkend="guc-compute-query-id"/>
924+
parameter is enabled or a third-party module that computes query
925+
identifiers is configured.
926+
</para></entry>
927+
</row>
928+
913929
<row>
914930
<entry role="catalog_table_entry"><para role="column_definition">
915931
<structfield>query</structfield> <type>text</type>

doc/src/sgml/ref/explain.sgml

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

src/backend/catalog/system_views.sql

+1
Original file line numberDiff line numberDiff line change
@@ -833,6 +833,7 @@ CREATE VIEW pg_stat_activity AS
833833
S.state,
834834
S.backend_xid,
835835
s.backend_xmin,
836+
S.queryid,
836837
S.query,
837838
S.backend_type
838839
FROM pg_stat_get_activity(NULL) AS S

src/backend/commands/explain.c

+18
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "nodes/extensible.h"
2525
#include "nodes/makefuncs.h"
2626
#include "nodes/nodeFuncs.h"
27+
#include "parser/analyze.h"
2728
#include "parser/parsetree.h"
2829
#include "rewrite/rewriteHandler.h"
2930
#include "storage/bufmgr.h"
@@ -165,6 +166,8 @@ ExplainQuery(ParseState *pstate, ExplainStmt *stmt,
165166
{
166167
ExplainState *es = NewExplainState();
167168
TupOutputState *tstate;
169+
JumbleState *jstate = NULL;
170+
Query *query;
168171
List *rewritten;
169172
ListCell *lc;
170173
bool timing_set = false;
@@ -241,6 +244,13 @@ ExplainQuery(ParseState *pstate, ExplainStmt *stmt,
241244
/* if the summary was not set explicitly, set default value */
242245
es->summary = (summary_set) ? es->summary : es->analyze;
243246

247+
query = castNode(Query, stmt->query);
248+
if (compute_query_id)
249+
jstate = JumbleQuery(query, pstate->p_sourcetext);
250+
251+
if (post_parse_analyze_hook)
252+
(*post_parse_analyze_hook) (pstate, query, jstate);
253+
244254
/*
245255
* Parse analysis was done already, but we still have to run the rule
246256
* rewriter. We do not do AcquireRewriteLocks: we assume the query either
@@ -600,6 +610,14 @@ ExplainOnePlan(PlannedStmt *plannedstmt, IntoClause *into, ExplainState *es,
600610
/* Create textual dump of plan tree */
601611
ExplainPrintPlan(es, queryDesc);
602612

613+
if (es->verbose && plannedstmt->queryId != UINT64CONST(0))
614+
{
615+
char buf[MAXINT8LEN+1];
616+
617+
pg_lltoa(plannedstmt->queryId, buf);
618+
ExplainPropertyText("Query Identifier", buf, es);
619+
}
620+
603621
/* Show buffer usage in planning */
604622
if (bufusage)
605623
{

src/backend/executor/execMain.c

+9
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
#include "storage/lmgr.h"
5959
#include "tcop/utility.h"
6060
#include "utils/acl.h"
61+
#include "utils/backend_status.h"
6162
#include "utils/lsyscache.h"
6263
#include "utils/memutils.h"
6364
#include "utils/partcache.h"
@@ -128,6 +129,14 @@ static void EvalPlanQualStart(EPQState *epqstate, Plan *planTree);
128129
void
129130
ExecutorStart(QueryDesc *queryDesc, int eflags)
130131
{
132+
/*
133+
* In some cases (e.g. an EXECUTE statement) a query execution will skip
134+
* parse analysis, which means that the queryid won't be reported. Note
135+
* that it's harmless to report the queryid multiple time, as the call will
136+
* be ignored if the top level queryid has already been reported.
137+
*/
138+
pgstat_report_queryid(queryDesc->plannedstmt->queryId, false);
139+
131140
if (ExecutorStart_hook)
132141
(*ExecutorStart_hook) (queryDesc, eflags);
133142
else

src/backend/executor/execParallel.c

+3-2
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ ExecSerializePlan(Plan *plan, EState *estate)
175175
*/
176176
pstmt = makeNode(PlannedStmt);
177177
pstmt->commandType = CMD_SELECT;
178-
pstmt->queryId = UINT64CONST(0);
178+
pstmt->queryId = pgstat_get_my_queryid();
179179
pstmt->hasReturning = false;
180180
pstmt->hasModifyingCTE = false;
181181
pstmt->canSetTag = true;
@@ -1421,8 +1421,9 @@ ParallelQueryMain(dsm_segment *seg, shm_toc *toc)
14211421
/* Setting debug_query_string for individual workers */
14221422
debug_query_string = queryDesc->sourceText;
14231423

1424-
/* Report workers' query for monitoring purposes */
1424+
/* Report workers' query and queryId for monitoring purposes */
14251425
pgstat_report_activity(STATE_RUNNING, debug_query_string);
1426+
pgstat_report_queryid(queryDesc->plannedstmt->queryId, false);
14261427

14271428
/* Attach to the dynamic shared memory area. */
14281429
area_space = shm_toc_lookup(toc, PARALLEL_KEY_DSA, false);

src/backend/parser/analyze.c

+5
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
#include "parser/parse_type.h"
4646
#include "parser/parsetree.h"
4747
#include "rewrite/rewriteManip.h"
48+
#include "utils/backend_status.h"
4849
#include "utils/builtins.h"
4950
#include "utils/guc.h"
5051
#include "utils/queryjumble.h"
@@ -130,6 +131,8 @@ parse_analyze(RawStmt *parseTree, const char *sourceText,
130131

131132
free_parsestate(pstate);
132133

134+
pgstat_report_queryid(query->queryId, false);
135+
133136
return query;
134137
}
135138

@@ -167,6 +170,8 @@ parse_analyze_varparams(RawStmt *parseTree, const char *sourceText,
167170

168171
free_parsestate(pstate);
169172

173+
pgstat_report_queryid(query->queryId, false);
174+
170175
return query;
171176
}
172177

0 commit comments

Comments
 (0)