On Thu, Feb 06, 2020 at 02:59:09PM -0500, Robert Haas wrote:
> On Wed, Feb 5, 2020 at 9:32 AM Julien Rouhaud <julien.rouh...@free.fr> wrote:
> > There's also the possibility to reserve 1 bit of the hash to know if
> > this is a utility command or not, although I don't recall right now
> > all the possible issues with utility commands and some special
> > handling of them.  I'll work on it before the next commitfest.
>
> FWIW, I don't really see why it would be bad to have 0 mean that
> "there's no query ID for some reason" without caring whether that's
> because the current statement is a utility statement or because
> there's no statement in progress at all or whatever else. The user
> probably doesn't need our help to distinguish between "no statement"
> and "utility statement", right?

Sure, but if we don't fix that it means that we also won't expose any queryid
for utility statement, even if pg_stat_statements is configured to track those
(with a very poor queryid handling, but still).

While looking at this again, I realized that pg_stat_statements doesn't compute
a queryid during the post parse analysis hook just to make sure that no query
identifier will be set during executorStart and the rest of executor functions.

AFAICT, that can't happen anyway since pg_plan_queries() will discard any
computed queryid for utility statements.  This seems to be an oversight due to
original pg_stat_statements implementation, so I fixed this.

Then, as processUtility is called between parse analysis and executor, I think
that we can simply work around this by computing utility statements query
identifier during parse analysis, removing it in pgss_ProcessUtility and
keeping a copy of it for the pgss_store calls in that function, as done in the
attached v5.

This fixes everything except EXECUTE statements, which has to get the
underlying query's queryid.  The problem is that EXECUTE won't get through
parse analysis, so while it's correctly handled for execution and pgss_store,
it's not being exposed in pg_stat_activity and log_line_prefix.  To fix it, I
added an extra call to pgstat_report_queryid in executorStart.  As this
function is a no-op if a queryid is already exposed, this shouldn't cause any
harm and fix any other cases of query execution that don't go through parse
analysis.

Finally, DEALLOCATE is entirely ignored by pg_stat_statements, so those
statements will always be reported with a NULL/0 queryid, but this is
consistent as it's also not present in pg_stat_statements() SRF.
diff --git a/contrib/pg_stat_statements/pg_stat_statements.c 
b/contrib/pg_stat_statements/pg_stat_statements.c
index 6f82a671ee..2da810ade6 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -112,6 +112,14 @@ static const uint32 PGSS_PG_MAJOR_VERSION = PG_VERSION_NUM 
/ 100;
 
 #define JUMBLE_SIZE                            1024    /* query serialization 
buffer size */
 
+/*
+ * 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
  */
@@ -308,7 +316,8 @@ static void pgss_ProcessUtility(PlannedStmt *pstmt, const 
char *queryString,
                                                                
ProcessUtilityContext context, ParamListInfo params,
                                                                
QueryEnvironment *queryEnv,
                                                                DestReceiver 
*dest, char *completionTag);
-static uint64 pgss_hash_string(const char *str, int len);
+static const char *pgss_clean_querytext(const char *query, int *location, int 
*len);
+static uint64 pgss_compute_utility_queryid(const char *query, int query_len);
 static void pgss_store(const char *query, uint64 queryId,
                                           int query_location, int query_len,
                                           double total_time, uint64 rows,
@@ -792,16 +801,34 @@ pgss_post_parse_analyze(ParseState *pstate, Query *query)
                return;
 
        /*
-        * Utility statements 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.
+        * We compute a queryId now so that it can get exported in out
+        * PgBackendStatus.  pgss_ProcessUtility will later discard it to 
prevents
+        * double counting of optimizable statements that are directly 
contained in
+        * utility statements.  Note that we don't compute a queryId for 
prepared
+        * statemets related utility, as those will inherit from the underlying
+        * statements's one (except DEALLOCATE which is entirely untracked).
         */
        if (query->utilityStmt)
        {
-               query->queryId = UINT64CONST(0);
+               if (pgss_track_utility && 
PGSS_HANDLED_UTILITY(query->utilityStmt)
+                       && pstate->p_sourcetext)
+               {
+                       const char *querytext = pstate->p_sourcetext;
+                       int query_location = query->stmt_location;
+                       int query_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.
+                        */
+                       querytext = pgss_clean_querytext(pstate->p_sourcetext,
+                                                                               
         &query_location,
+                                                                               
         &query_len);
+
+                       query->queryId = 
pgss_compute_utility_queryid(querytext, query_len);
+               }
+               else
+                       query->queryId = UINT64CONST(0);
                return;
        }
 
@@ -963,6 +990,23 @@ pgss_ProcessUtility(PlannedStmt *pstmt, const char 
*queryString,
                                        DestReceiver *dest, char *completionTag)
 {
        Node       *parsetree = pstmt->utilityStmt;
+       uint64          saved_queryId = pstmt->queryId;
+
+       /*
+        * Utility statements 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() && pgss_track_utility)
+               pstmt->queryId = UINT64CONST(0);
 
        /*
         * If it's an EXECUTE statement, we don't track it and don't increment 
the
@@ -979,9 +1023,7 @@ pgss_ProcessUtility(PlannedStmt *pstmt, const char 
*queryString,
         * Likewise, we don't track execution of DEALLOCATE.
         */
        if (pgss_track_utility && pgss_enabled() &&
-               !IsA(parsetree, ExecuteStmt) &&
-               !IsA(parsetree, PrepareStmt) &&
-               !IsA(parsetree, DeallocateStmt))
+               PGSS_HANDLED_UTILITY(parsetree))
        {
                instr_time      start;
                instr_time      duration;
@@ -1047,7 +1089,7 @@ pgss_ProcessUtility(PlannedStmt *pstmt, const char 
*queryString,
                INSTR_TIME_SUBTRACT(bufusage.blk_write_time, 
bufusage_start.blk_write_time);
 
                pgss_store(queryString,
-                                  0,                   /* signal that it's a 
utility stmt */
+                                  saved_queryId,
                                   pstmt->stmt_location,
                                   pstmt->stmt_len,
                                   INSTR_TIME_GET_MILLISEC(duration),
@@ -1069,22 +1111,76 @@ 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.
+ * Given a possibly multi-statement source string, confine our attention to the
+ * relevant part of the string.
+ */
+static const char *
+pgss_clean_querytext(const char *query, int *location, int *len)
+{
+       int query_location = *location;
+       int query_len = *len;
+
+       /* 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--;
+
+       *location = query_location;
+       *len = query_len;
+
+       return query;
+}
+
+/*
+ * Compute a query identifier for the given utility query string.
  */
 static uint64
-pgss_hash_string(const char *str, int len)
+pgss_compute_utility_queryid(const char *str, int query_len)
 {
-       return DatumGetUInt64(hash_any_extended((const unsigned char *) str,
-                                                                               
        len, 0));
+       uint64 queryId;
+
+       queryId = DatumGetUInt64(hash_any_extended((const unsigned char *) str,
+                                                                               
           query_len, 0));
+
+       /*
+        * If we are unlucky enough to get a hash of zero(invalid), use
+        * queryID as 2 instead, queryID 1 is already in use for normal
+        * statements.
+        */
+       if (queryId == UINT64CONST(0))
+               queryId = UINT64CONST(2);
+
+       return queryId;
 }
 
 /*
  * Store some statistics for a statement.
  *
- * If queryId is 0 then this is a utility statement and we should compute
- * a suitable queryId internally.
+ * If queryId is 0 then this is a utility statement for which we couldn't
+ * 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
  * we have no statistics as yet; we just want to record the normalized
@@ -1111,50 +1207,15 @@ pgss_store(const char *query, uint64 queryId,
        /*
         * Confine our attention to the relevant part of the string, if the 
query
         * is a portion of a multi-statement source string.
-        *
-        * 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--;
+       query = pgss_clean_querytext(query, &query_location, &query_len);
 
        /*
-        * For utility statements, we just hash the query string to get an ID.
+        * For not already handled utility statements, we just hash the query
+        * string to get an ID.
         */
        if (queryId == UINT64CONST(0))
-       {
-               queryId = pgss_hash_string(query, query_len);
-
-               /*
-                * If we are unlucky enough to get a hash of zero(invalid), use
-                * queryID as 2 instead, queryID 1 is already in use for normal
-                * statements.
-                */
-               if (queryId == UINT64CONST(0))
-                       queryId = UINT64CONST(2);
-       }
+               queryId = pgss_compute_utility_queryid(query, query_len);
 
        /* Set up key for hashtable search */
        key.userid = GetUserId();
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index c1128f89ec..52faea72ce 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -6552,6 +6552,11 @@ local0.*    /var/log/postgresql
              session processes</entry>
              <entry>no</entry>
             </row>
+            <row>
+             <entry><literal>%Q</literal></entry>
+             <entry>queryid: identifier of session's current query, if 
any</entry>
+             <entry>yes</entry>
+            </row>
             <row>
              <entry><literal>%%</literal></entry>
              <entry>Literal <literal>%</literal></entry>
@@ -6960,8 +6965,8 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH 
csv;
       <listitem>
        <para>
         Enables the collection of information on the currently
-        executing command of each session, along with the time when
-        that command began execution. This parameter is on by
+        executing command of each session, along with its identifier and the
+        time when that command began execution. This parameter is on by
         default. Note that even when enabled, this information is not
         visible to all users, only to superusers and the user owning
         the session being reported on, so it should not represent a
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 08353cb343..b9ebf3539d 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -845,6 +845,18 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   
11:34   0:00 postgres: ser
      <entry><type>xid</type></entry>
      <entry>The current backend's <literal>xmin</literal> horizon.</entry>
     </row>
+    <row>
+     <entry><structfield>queryid</structfield></entry>
+     <entry><type>bigint</type></entry>
+     <entry>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.  By
+      default, query identifiers are not computed, so this field will always
+      be null, unless an additional module that compute query identifiers, such
+      as <xref linkend="pgstatstatements"/>, is configured.
+     </entry>
+    </row>
     <row>
      <entry><structfield>query</structfield></entry>
      <entry><type>text</type></entry>
diff --git a/src/backend/catalog/system_views.sql 
b/src/backend/catalog/system_views.sql
index f681aafcf9..b953932b03 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -757,6 +757,7 @@ CREATE VIEW pg_stat_activity AS
             S.state,
             S.backend_xid,
             s.backend_xmin,
+            S.queryid,
             S.query,
             S.backend_type
     FROM pg_stat_get_activity(NULL) AS S
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index ee5c3a60ff..fd7346919b 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -54,6 +54,7 @@
 #include "mb/pg_wchar.h"
 #include "miscadmin.h"
 #include "parser/parsetree.h"
+#include "pgstat.h"
 #include "storage/bufmgr.h"
 #include "storage/lmgr.h"
 #include "tcop/utility.h"
@@ -142,6 +143,13 @@ static void EvalPlanQualStart(EPQState *epqstate, Plan 
*planTree);
 void
 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)
                (*ExecutorStart_hook) (queryDesc, eflags);
        else
diff --git a/src/backend/executor/execParallel.c 
b/src/backend/executor/execParallel.c
index a753d6efa0..c5c02a1d2f 100644
--- a/src/backend/executor/execParallel.c
+++ b/src/backend/executor/execParallel.c
@@ -121,7 +121,7 @@ typedef struct ExecParallelInitializeDSMContext
 } ExecParallelInitializeDSMContext;
 
 /* Helper functions that run in the parallel leader. */
-static char *ExecSerializePlan(Plan *plan, EState *estate);
+static char *ExecSerializePlan(Plan *plan, EState *estate, uint64 queryId);
 static bool ExecParallelEstimate(PlanState *node,
                                                                 
ExecParallelEstimateContext *e);
 static bool ExecParallelInitializeDSM(PlanState *node,
@@ -140,7 +140,7 @@ static DestReceiver *ExecParallelGetReceiver(dsm_segment 
*seg, shm_toc *toc);
  * Create a serialized representation of the plan to be sent to each worker.
  */
 static char *
-ExecSerializePlan(Plan *plan, EState *estate)
+ExecSerializePlan(Plan *plan, EState *estate, uint64 queryId)
 {
        PlannedStmt *pstmt;
        ListCell   *lc;
@@ -171,7 +171,7 @@ ExecSerializePlan(Plan *plan, EState *estate)
         */
        pstmt = makeNode(PlannedStmt);
        pstmt->commandType = CMD_SELECT;
-       pstmt->queryId = UINT64CONST(0);
+       pstmt->queryId = queryId;
        pstmt->hasReturning = false;
        pstmt->hasModifyingCTE = false;
        pstmt->canSetTag = true;
@@ -562,7 +562,8 @@ ExecParallelSetupTupleQueues(ParallelContext *pcxt, bool 
reinitialize)
 ParallelExecutorInfo *
 ExecInitParallelPlan(PlanState *planstate, EState *estate,
                                         Bitmapset *sendParams, int nworkers,
-                                        int64 tuples_needed)
+                                        int64 tuples_needed,
+                                        uint64 queryId)
 {
        ParallelExecutorInfo *pei;
        ParallelContext *pcxt;
@@ -603,7 +604,7 @@ ExecInitParallelPlan(PlanState *planstate, EState *estate,
        pei->planstate = planstate;
 
        /* Fix up and serialize plan to be sent to workers. */
-       pstmt_data = ExecSerializePlan(planstate->plan, estate);
+       pstmt_data = ExecSerializePlan(planstate->plan, estate, queryId);
 
        /* Create a parallel context. */
        pcxt = CreateParallelContext("postgres", "ParallelQueryMain", nworkers);
@@ -1357,8 +1358,9 @@ ParallelQueryMain(dsm_segment *seg, shm_toc *toc)
        /* Setting debug_query_string for individual workers */
        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_queryid(queryDesc->plannedstmt->queryId, false);
 
        /* Attach to the dynamic shared memory area. */
        area_space = shm_toc_lookup(toc, PARALLEL_KEY_DSA, false);
diff --git a/src/backend/executor/nodeGather.c 
b/src/backend/executor/nodeGather.c
index 6b8ed867d5..c57e197020 100644
--- a/src/backend/executor/nodeGather.c
+++ b/src/backend/executor/nodeGather.c
@@ -172,7 +172,8 @@ ExecGather(PlanState *pstate)
                                                                                
                 estate,
                                                                                
                 gather->initParam,
                                                                                
                 gather->num_workers,
-                                                                               
                 node->tuples_needed);
+                                                                               
                 node->tuples_needed,
+                                                                               
                 pgstat_get_my_queryid());
                        else
                                ExecParallelReinitialize(node->ps.lefttree,
                                                                                
 node->pei,
diff --git a/src/backend/executor/nodeGatherMerge.c 
b/src/backend/executor/nodeGatherMerge.c
index 317ddb4ae2..b2040dca8e 100644
--- a/src/backend/executor/nodeGatherMerge.c
+++ b/src/backend/executor/nodeGatherMerge.c
@@ -24,6 +24,7 @@
 #include "lib/binaryheap.h"
 #include "miscadmin.h"
 #include "optimizer/optimizer.h"
+#include "pgstat.h"
 #include "utils/memutils.h"
 #include "utils/rel.h"
 
@@ -216,7 +217,8 @@ ExecGatherMerge(PlanState *pstate)
                                                                                
                 estate,
                                                                                
                 gm->initParam,
                                                                                
                 gm->num_workers,
-                                                                               
                 node->tuples_needed);
+                                                                               
                 node->tuples_needed,
+                                                                               
                 pgstat_get_my_queryid());
                        else
                                ExecParallelReinitialize(node->ps.lefttree,
                                                                                
 node->pei,
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index 748bebffc1..712d48d5bb 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -43,6 +43,7 @@
 #include "parser/parse_relation.h"
 #include "parser/parse_target.h"
 #include "parser/parsetree.h"
+#include "pgstat.h"
 #include "rewrite/rewriteManip.h"
 #include "utils/rel.h"
 
@@ -120,6 +121,8 @@ parse_analyze(RawStmt *parseTree, const char *sourceText,
 
        free_parsestate(pstate);
 
+       pgstat_report_queryid(query->queryId, false);
+
        return query;
 }
 
@@ -153,6 +156,8 @@ parse_analyze_varparams(RawStmt *parseTree, const char 
*sourceText,
 
        free_parsestate(pstate);
 
+       pgstat_report_queryid(query->queryId, false);
+
        return query;
 }
 
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index 7169509a79..bcd119f160 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -3141,6 +3141,7 @@ pgstat_report_activity(BackendState state, const char 
*cmd_str)
                        beentry->st_activity_start_timestamp = 0;
                        /* st_xact_start_timestamp and wait_event_info are also 
disabled */
                        beentry->st_xact_start_timestamp = 0;
+                       beentry->st_queryid = 0;
                        proc->wait_event_info = 0;
                        PGSTAT_END_WRITE_ACTIVITY(beentry);
                }
@@ -3171,6 +3172,14 @@ pgstat_report_activity(BackendState state, const char 
*cmd_str)
        beentry->st_state = state;
        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 = 0;
+
        if (cmd_str != NULL)
        {
                memcpy((char *) beentry->st_activity_raw, cmd_str, len);
@@ -3181,6 +3190,48 @@ pgstat_report_activity(BackendState state, const char 
*cmd_str)
        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 (!beentry)
+               return;
+
+       /*
+        * if track_activities is disabled, st_queryid should already have been
+        * reset
+        */
+       if (!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_progress_start_command() -
  *
@@ -4754,6 +4805,20 @@ pgstat_get_db_entry(Oid databaseid, bool create)
        return result;
 }
 
+/* ----------
+ * pgstat_get_my_queryid() -
+ *
+ *     Return current backend's query identifier.
+ */
+uint64
+pgstat_get_my_queryid(void)
+{
+       if (!MyBEEntry)
+               return 0;
+
+       return MyBEEntry->st_queryid;
+}
+
 
 /*
  * Lookup the hash table entry for the specified table. If no hash
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 0a6f80963b..4ad39c5845 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -742,6 +742,8 @@ pg_analyze_and_rewrite_params(RawStmt *parsetree,
 
        free_parsestate(pstate);
 
+       pgstat_report_queryid(query->queryId, false);
+
        if (log_parser_stats)
                ShowUsage("PARSE ANALYSIS STATISTICS");
 
@@ -958,6 +960,7 @@ pg_plan_queries(List *querytrees, int cursorOptions, 
ParamListInfo boundParams)
                        stmt->utilityStmt = query->utilityStmt;
                        stmt->stmt_location = query->stmt_location;
                        stmt->stmt_len = query->stmt_len;
+                       stmt->queryId = query->queryId;
                }
                else
                {
@@ -1073,6 +1076,8 @@ exec_simple_query(const char *query_string)
                DestReceiver *receiver;
                int16           format;
 
+               pgstat_report_queryid(0, true);
+
                /*
                 * Get the command name for use in status display (it also 
becomes the
                 * default completion tag, down inside PortalRun).  Set 
ps_status and
diff --git a/src/backend/utils/adt/pgstatfuncs.c 
b/src/backend/utils/adt/pgstatfuncs.c
index 7e6a3c1774..9f7dd372ef 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -547,7 +547,7 @@ pg_stat_get_progress_info(PG_FUNCTION_ARGS)
 Datum
 pg_stat_get_activity(PG_FUNCTION_ARGS)
 {
-#define PG_STAT_GET_ACTIVITY_COLS      30
+#define PG_STAT_GET_ACTIVITY_COLS      31
        int                     num_backends = pgstat_fetch_stat_numbackends();
        int                     curr_backend;
        int                     pid = PG_ARGISNULL(0) ? -1 : PG_GETARG_INT32(0);
@@ -888,6 +888,10 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
                                values[28] = BoolGetDatum(false);       /* GSS 
Encryption not in
                                                                                
                         * use */
                        }
+                       if (beentry->st_queryid == 0)
+                               nulls[30] = true;
+                       else
+                               values[30] = 
DatumGetUInt64(beentry->st_queryid);
                }
                else
                {
@@ -916,6 +920,7 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
                        nulls[27] = true;
                        nulls[28] = true;
                        nulls[29] = true;
+                       nulls[30] = true;
                }
 
                tuplestore_putvalues(tupstore, tupdesc, values, nulls);
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index f5b0211f66..678c43fd45 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -72,10 +72,10 @@
 #include "libpq/pqformat.h"
 #include "mb/pg_wchar.h"
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "postmaster/postmaster.h"
 #include "postmaster/syslogger.h"
 #include "storage/ipc.h"
-#include "storage/proc.h"
 #include "tcop/tcopprot.h"
 #include "utils/guc.h"
 #include "utils/memutils.h"
@@ -2702,6 +2702,14 @@ log_line_prefix(StringInfo buf, ErrorData *edata)
                                else
                                        appendStringInfoString(buf, 
unpack_sql_state(edata->sqlerrcode));
                                break;
+                       case 'Q':
+                               if (padding != 0)
+                                       appendStringInfo(buf, "%*ld", padding,
+                                                       
pgstat_get_my_queryid());
+                               else
+                                       appendStringInfo(buf, "%ld",
+                                                       
pgstat_get_my_queryid());
+                               break;
                        default:
                                /* format error - ignore it */
                                break;
diff --git a/src/backend/utils/misc/postgresql.conf.sample 
b/src/backend/utils/misc/postgresql.conf.sample
index e1048c0047..63491299f9 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -532,6 +532,7 @@
                                        #   %t = timestamp without milliseconds
                                        #   %m = timestamp with milliseconds
                                        #   %n = timestamp with milliseconds 
(as a Unix epoch)
+                                       #   %Q = query ID (0 if none or not 
computed)
                                        #   %i = command tag
                                        #   %e = SQL state
                                        #   %c = session ID
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 226c904c04..508998fac8 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5175,9 +5175,9 @@
   proname => 'pg_stat_get_activity', prorows => '100', proisstrict => 'f',
   proretset => 't', provolatile => 's', proparallel => 'r',
   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,bool,text,numeric,text,bool,text,bool,int4}',
-  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,sslcompression,ssl_client_dn,ssl_client_serial,ssl_issuer_dn,gss_auth,gss_princ,gss_enc,leader_pid}',
+  proallargtypes => 
'{int4,oid,int4,oid,text,text,text,text,text,timestamptz,timestamptz,timestamptz,timestamptz,inet,text,int4,xid,xid,text,bool,text,text,int4,bool,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,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,sslcompression,ssl_client_dn,ssl_client_serial,ssl_issuer_dn,gss_auth,gss_princ,gss_enc,leader_pid,
 queryid}',
   prosrc => 'pg_stat_get_activity' },
 { oid => '3318',
   descr => 'statistics: information about progress of backends running 
maintenance command',
diff --git a/src/include/executor/execParallel.h 
b/src/include/executor/execParallel.h
index 17d07cf020..63bb80c00c 100644
--- a/src/include/executor/execParallel.h
+++ b/src/include/executor/execParallel.h
@@ -38,7 +38,8 @@ typedef struct ParallelExecutorInfo
 
 extern ParallelExecutorInfo *ExecInitParallelPlan(PlanState *planstate,
                                                                                
                  EState *estate, Bitmapset *sendParam, int nworkers,
-                                                                               
                  int64 tuples_needed);
+                                                                               
                  int64 tuples_needed,
+                                                                               
                  uint64 queryId);
 extern void ExecParallelCreateReaders(ParallelExecutorInfo *pei);
 extern void ExecParallelFinish(ParallelExecutorInfo *pei);
 extern void ExecParallelCleanup(ParallelExecutorInfo *pei);
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index aecb6013f0..9a11aec4ee 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -1098,6 +1098,9 @@ typedef struct PgBackendStatus
        ProgressCommandType st_progress_command;
        Oid                     st_progress_command_target;
        int64           st_progress_param[PGSTAT_NUM_PROGRESS_PARAM];
+
+       /* query identifier, optionnally computed using post_parse_analyze_hook 
*/
+       uint64          st_queryid;
 } PgBackendStatus;
 
 /*
@@ -1277,6 +1280,7 @@ extern void pgstat_initialize(void);
 extern void pgstat_bestart(void);
 
 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_appname(const char *appname);
 extern void pgstat_report_xact_timestamp(TimestampTz tstamp);
@@ -1286,6 +1290,7 @@ extern const char 
*pgstat_get_backend_current_activity(int pid, bool checkUser);
 extern const char *pgstat_get_crashed_backend_activity(int pid, char *buffer,
                                                                                
                           int buflen);
 extern const char *pgstat_get_backend_desc(BackendType backendType);
+extern uint64 pgstat_get_my_queryid(void);
 
 extern void pgstat_progress_start_command(ProgressCommandType cmdtype,
                                                                                
  Oid relid);
diff --git a/src/test/regress/expected/rules.out 
b/src/test/regress/expected/rules.out
index 634f8256f7..0a92797777 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1746,9 +1746,10 @@ pg_stat_activity| SELECT s.datid,
     s.state,
     s.backend_xid,
     s.backend_xmin,
+    s.queryid,
     s.query,
     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, sslcompression, 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, sslcompression, 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_authid u ON ((s.usesysid = u.oid)));
 pg_stat_all_indexes| SELECT c.oid AS relid,
@@ -1852,7 +1853,7 @@ pg_stat_gssapi| SELECT s.pid,
     s.gss_auth AS gss_authenticated,
     s.gss_princ AS principal,
     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, sslcompression, 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, sslcompression, 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);
 pg_stat_progress_analyze| SELECT s.pid,
     s.datid,
@@ -1985,7 +1986,7 @@ pg_stat_replication| SELECT s.pid,
     w.spill_txns,
     w.spill_count,
     w.spill_bytes
-   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, sslcompression, 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, sslcompression, 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, spill_txns, spill_count, spill_bytes) ON ((s.pid = 
w.pid)))
      LEFT JOIN pg_authid u ON ((s.usesysid = u.oid)));
 pg_stat_ssl| SELECT s.pid,
@@ -1997,7 +1998,7 @@ pg_stat_ssl| SELECT s.pid,
     s.ssl_client_dn AS client_dn,
     s.ssl_client_serial AS client_serial,
     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, sslcompression, 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, sslcompression, 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);
 pg_stat_subscription| SELECT su.oid AS subid,
     su.subname,

Reply via email to