Make use of in-core query id added by commit 5fd9dfa5f5
authorBruce Momjian <bruce@momjian.us>
Wed, 7 Apr 2021 18:03:56 +0000 (14:03 -0400)
committerBruce Momjian <bruce@momjian.us>
Wed, 7 Apr 2021 18:04:06 +0000 (14:04 -0400)
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
21 files changed:
contrib/pg_stat_statements/pg_stat_statements.c
doc/src/sgml/config.sgml
doc/src/sgml/monitoring.sgml
doc/src/sgml/ref/explain.sgml
src/backend/catalog/system_views.sql
src/backend/commands/explain.c
src/backend/executor/execMain.c
src/backend/executor/execParallel.c
src/backend/parser/analyze.c
src/backend/tcop/postgres.c
src/backend/utils/activity/backend_status.c
src/backend/utils/adt/pgstatfuncs.c
src/backend/utils/error/elog.c
src/backend/utils/misc/postgresql.conf.sample
src/backend/utils/misc/queryjumble.c
src/include/catalog/catversion.h
src/include/catalog/pg_proc.dat
src/include/utils/backend_status.h
src/test/regress/expected/explain.out
src/test/regress/expected/rules.out
src/test/regress/sql/explain.sql

index 0f8bac0ccae429e0bbddfea9b88c304b5c615738..52cba86196971dbab872a2b86d4df518cea6850e 100644 (file)
@@ -67,6 +67,7 @@
 #include "tcop/utility.h"
 #include "utils/acl.h"
 #include "utils/builtins.h"
+#include "utils/queryjumble.h"
 #include "utils/memutils.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 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
  */
@@ -309,7 +318,6 @@ static void pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
                                ProcessUtilityContext context, ParamListInfo params,
                                QueryEnvironment *queryEnv,
                                DestReceiver *dest, QueryCompletion *qc);
-static uint64 pgss_hash_string(const char *str, int len);
 static void pgss_store(const char *query, uint64 queryId,
                       int query_location, int query_len,
                       pgssStoreKind kind,
@@ -806,16 +814,14 @@ pgss_post_parse_analyze(ParseState *pstate, Query *query, JumbleState *jstate)
        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.
+    * Clear queryId for prepared statements related utility, as those will
+    * inherit from the underlying statement's one (except DEALLOCATE which is
+    * entirely untracked).
     */
    if (query->utilityStmt)
    {
-       query->queryId = UINT64CONST(0);
+       if (pgss_track_utility && !PGSS_HANDLED_UTILITY(query->utilityStmt))
+           query->queryId = UINT64CONST(0);
        return;
    }
 
@@ -1057,6 +1063,23 @@ pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
                    DestReceiver *dest, QueryCompletion *qc)
 {
    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
@@ -1073,9 +1096,7 @@ pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
     * Likewise, we don't track execution of DEALLOCATE.
     */
    if (pgss_track_utility && pgss_enabled(exec_nested_level) &&
-       !IsA(parsetree, ExecuteStmt) &&
-       !IsA(parsetree, PrepareStmt) &&
-       !IsA(parsetree, DeallocateStmt))
+       PGSS_HANDLED_UTILITY(parsetree))
    {
        instr_time  start;
        instr_time  duration;
@@ -1130,7 +1151,7 @@ pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
        WalUsageAccumDiff(&walusage, &pgWalUsage, &walusage_start);
 
        pgss_store(queryString,
-                  0,           /* signal that it's a utility stmt */
+                  saved_queryId,
                   pstmt->stmt_location,
                   pstmt->stmt_len,
                   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.
  *
- * 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
@@ -1200,52 +1210,18 @@ pgss_store(const char *query, uint64 queryId,
        return;
 
    /*
-    * 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.
+    * Nothing to do if compute_query_id isn't enabled and no other module
+    * computed a query identifier.
     */
-   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--;
+   if (queryId == UINT64CONST(0))
+       return;
 
    /*
-    * For utility statements, we just hash the query string to get an ID.
+    * Confine our attention to the relevant part of the string, if the query
+    * is a portion of a multi-statement source string, and update query
+    * location and length if needed.
     */
-   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);
-   }
+   query = CleanQuerytext(query, &query_location, &query_len);
 
    /* Set up key for hashtable search */
    key.userid = GetUserId();
index 9d846cb7be0f67e4cc2891b348c260d27fa9eeb7..18447f404ce79477b0eb59f6df4f37570a6de2ec 100644 (file)
@@ -7004,6 +7004,15 @@ local0.*    /var/log/postgresql
              session processes</entry>
              <entry>no</entry>
             </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>
              <entry><literal>%%</literal></entry>
              <entry>Literal <literal>%</literal></entry>
@@ -7480,8 +7489,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
@@ -7630,12 +7639,16 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
       </term>
       <listitem>
        <para>
-        Enables in-core computation of a query identifier.  The <xref
-        linkend="pgstatstatements"/> extension requires a query identifier
-        to be computed.  Note that an external module can alternatively
-        be used if the in-core query identifier computation method
-        isn't acceptable.  In this case, in-core computation should
-        remain disabled.  The default is <literal>off</literal>.
+        Enables in-core computation of a query identifier.
+        Query identifiers can be displayed in the <link
+        linkend="monitoring-pg-stat-activity-view"><structname>pg_stat_activity</structname></link>
+        view, using <command>EXPLAIN</command>, or emitted in the log if
+        configured via the <xref linkend="guc-log-line-prefix"/> parameter.
+        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>
        <note>
         <para>
index 56018745c8993e9143f6fd2753a158b4a6e4611e..52958b4fd91d52962dabc5f556c27be10c480c34 100644 (file)
@@ -910,6 +910,22 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
       </para></entry>
      </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>
       <entry role="catalog_table_entry"><para role="column_definition">
        <structfield>query</structfield> <type>text</type>
index c4512332a068b57dcfc001c7abd5835db2597b64..4d758fb237e3994d56bedcbcb5642837953fa0bf 100644 (file)
@@ -136,8 +136,10 @@ ROLLBACK;
       the output column list for each node in the plan tree, schema-qualify
       table and function names, always label variables in expressions with
       their range table alias, and always print the name of each trigger for
-      which statistics are displayed.  This parameter defaults to
-      <literal>FALSE</literal>.
+      which statistics are displayed.  The query identifier will also be
+      displayed if one has been computed, see <xref
+      linkend="guc-compute-query-id"/> for more details.  This parameter
+      defaults to <literal>FALSE</literal>.
      </para>
     </listitem>
    </varlistentry>
index 5f2541d316dd12c5c013609959280ab689154a25..4d6b23278723aa289840f19bd555a63a4654d037 100644 (file)
@@ -833,6 +833,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
index ede8cec9472b36a83a2833daede8856da0a3c1f1..b62a76e7e5a6723b3d51ad6e241c24913637dbc6 100644 (file)
@@ -24,6 +24,7 @@
 #include "nodes/extensible.h"
 #include "nodes/makefuncs.h"
 #include "nodes/nodeFuncs.h"
+#include "parser/analyze.h"
 #include "parser/parsetree.h"
 #include "rewrite/rewriteHandler.h"
 #include "storage/bufmgr.h"
@@ -165,6 +166,8 @@ ExplainQuery(ParseState *pstate, ExplainStmt *stmt,
 {
    ExplainState *es = NewExplainState();
    TupOutputState *tstate;
+   JumbleState *jstate = NULL;
+   Query       *query;
    List       *rewritten;
    ListCell   *lc;
    bool        timing_set = false;
@@ -241,6 +244,13 @@ ExplainQuery(ParseState *pstate, ExplainStmt *stmt,
    /* if the summary was not set explicitly, set default value */
    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
     * 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 */
    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 */
    if (bufusage)
    {
index 78ddbf95f6805574771694efd39f4fc648a79e1f..b2e2df877331248977624fe9329d148942940d03 100644 (file)
@@ -58,6 +58,7 @@
 #include "storage/lmgr.h"
 #include "tcop/utility.h"
 #include "utils/acl.h"
+#include "utils/backend_status.h"
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
 #include "utils/partcache.h"
@@ -128,6 +129,14 @@ 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
index 366d0b20b92b2837d318916546e9d79bbbe0d8d1..c7a2f3147353e79eab5fbad15a153d3e1f3c69d6 100644 (file)
@@ -175,7 +175,7 @@ ExecSerializePlan(Plan *plan, EState *estate)
     */
    pstmt = makeNode(PlannedStmt);
    pstmt->commandType = CMD_SELECT;
-   pstmt->queryId = UINT64CONST(0);
+   pstmt->queryId = pgstat_get_my_queryid();
    pstmt->hasReturning = false;
    pstmt->hasModifyingCTE = false;
    pstmt->canSetTag = true;
@@ -1421,8 +1421,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);
index d6da20ee8c58c3ac977abb48ffa451795d53a67f..9ddf78dccdb45471f5394cd64325fb100764dc9f 100644 (file)
@@ -45,6 +45,7 @@
 #include "parser/parse_type.h"
 #include "parser/parsetree.h"
 #include "rewrite/rewriteManip.h"
+#include "utils/backend_status.h"
 #include "utils/builtins.h"
 #include "utils/guc.h"
 #include "utils/queryjumble.h"
@@ -130,6 +131,8 @@ parse_analyze(RawStmt *parseTree, const char *sourceText,
 
    free_parsestate(pstate);
 
+   pgstat_report_queryid(query->queryId, false);
+
    return query;
 }
 
@@ -167,6 +170,8 @@ parse_analyze_varparams(RawStmt *parseTree, const char *sourceText,
 
    free_parsestate(pstate);
 
+   pgstat_report_queryid(query->queryId, false);
+
    return query;
 }
 
index 50f2f7f2465bd1c4a7dce611f18d2af227d1289d..ef8fb20429c93b430d8420ee5fceb561d97405be 100644 (file)
@@ -695,6 +695,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");
 
@@ -913,6 +915,7 @@ pg_plan_queries(List *querytrees, const char *query_string, int cursorOptions,
            stmt->utilityStmt = query->utilityStmt;
            stmt->stmt_location = query->stmt_location;
            stmt->stmt_len = query->stmt_len;
+           stmt->queryId = query->queryId;
        }
        else
        {
@@ -1029,6 +1032,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
index a25ec0ee3c110ad23f6d0e716c740e587e96b91e..6110113e56a6813040929ea038f3bee861707480 100644 (file)
@@ -544,6 +544,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 = UINT64CONST(0);
            proc->wait_event_info = 0;
            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_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)
    {
        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_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() -
  *
@@ -972,6 +1021,25 @@ pgstat_get_crashed_backend_activity(int pid, char *buffer, int buflen)
    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() -
index 9ffbca685cd93e5c00bafda21d41f61897225b11..9fa4a93162f8d37aaccd94f1b50cc538963e0938 100644 (file)
@@ -569,7 +569,7 @@ pg_stat_get_progress_info(PG_FUNCTION_ARGS)
 Datum
 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         curr_backend;
    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
                                                     * use */
            }
+           if (beentry->st_queryid == 0)
+               nulls[29] = true;
+           else
+               values[29] = DatumGetUInt64(beentry->st_queryid);
        }
        else
        {
@@ -941,6 +945,7 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
            nulls[26] = true;
            nulls[27] = true;
            nulls[28] = true;
+           nulls[29] = true;
        }
 
        tuplestore_putvalues(tupstore, tupdesc, values, nulls);
index 12de4b38cbac285c50061bf2d70e8ef2a0e48bf1..1cf71a649b36bdf0a102ba493af333abec9b6004 100644 (file)
@@ -2714,6 +2714,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;
index 192577a02e5c578272d6e0cc09e05b8dec5cbd2c..65f61869663e5bb20137e1538c1cebc8b1cb2e92 100644 (file)
                    #   %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
index 2a47688fd63446503ed5e1ad6e5e286cea8c5c78..53286bb333f9306824624f27dbb2eb0d55f496d9 100644 (file)
@@ -39,7 +39,7 @@
 
 #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,
                         const unsigned char *item, Size size);
 static void JumbleQueryInternal(JumbleState *jstate, Query *query);
@@ -97,17 +97,9 @@ JumbleQuery(Query *query, const char *querytext)
    JumbleState *jstate = NULL;
    if (query->utilityStmt)
    {
-       const char *sql;
-       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.
-        */
-       sql = CleanQuerytext(querytext, &query_location, &query_len);
-
-       query->queryId = compute_utility_queryid(sql, query_len);
+       query->queryId = compute_utility_queryid(querytext,
+                                                query->stmt_location,
+                                                query->stmt_len);
    }
    else
    {
@@ -143,11 +135,18 @@ JumbleQuery(Query *query, const char *querytext)
  * Compute a query identifier for the given utility query string.
  */
 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;
+   const char *sql;
+
+   /*
+    * 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 *) str,
+   queryId = DatumGetUInt64(hash_any_extended((const unsigned char *) sql,
                                               query_len, 0));
 
    /*
index 4d534428d4e005c3cfff7d51175b57e51cdad699..53fd6c08b3386dd37c4871ad28432e385d4de805 100644 (file)
@@ -53,6 +53,6 @@
  */
 
 /*                         yyyymmddN */
-#define CATALOG_VERSION_NO 202104062
+#define CATALOG_VERSION_NO 202104071
 
 #endif
index 4309fa40dd2d23f5374e96a80022fdfe5c57bc78..97d8238a99761c7b2112594864c73355c635ca96 100644 (file)
   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,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}',
-  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}',
+  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,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,queryid}',
   prosrc => 'pg_stat_get_activity' },
 { oid => '3318',
   descr => 'statistics: information about progress of backends running maintenance command',
index 3fd7370d41e3ca6f5030153cfb2de8d7cd4d67bc..8e149b56ca11375703b456f85856b1ff3f94c997 100644 (file)
@@ -165,6 +165,9 @@ typedef struct PgBackendStatus
    ProgressCommandType st_progress_command;
    Oid         st_progress_command_target;
    int64       st_progress_param[PGSTAT_NUM_PROGRESS_PARAM];
+
+   /* query identifier, optionally computed using post_parse_analyze_hook */
+   uint64      st_queryid;
 } PgBackendStatus;
 
 
@@ -294,12 +297,14 @@ extern void pgstat_clear_backend_activity_snapshot(void);
 
 /* Activity reporting functions */
 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);
 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 uint64 pgstat_get_my_queryid(void);
 
 
 /* ----------
index b89b99fb020de193d16ea026dcd6c4f97aeb1e18..4c578d4f5e2332d18a3b1efe9c8142b1dd3bad52 100644 (file)
@@ -17,7 +17,7 @@ begin
     for ln in execute $1
     loop
         -- 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
         ln := regexp_replace(ln, '\m\d+kB', 'NkB', 'g');
         -- Ignore text-mode buffers output because it varies depending
@@ -477,3 +477,12 @@ select jsonb_pretty(
 (1 row)
 
 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)
+
index 9b59a7b4a5797708e516f03f5ad119b74cae1e99..264deda7af59d58a956255ff92d437fb6bccb665 100644 (file)
@@ -1762,9 +1762,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, 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_authid u ON ((s.usesysid = u.oid)));
 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_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, 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);
 pg_stat_progress_analyze| SELECT s.pid,
     s.datid,
@@ -2046,7 +2047,7 @@ pg_stat_replication| SELECT s.pid,
     w.sync_priority,
     w.sync_state,
     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)))
      LEFT JOIN pg_authid u ON ((s.usesysid = u.oid)));
 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_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, 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);
 pg_stat_subscription| SELECT su.oid AS subid,
     su.subname,
index f2eab030d614288eb31b331bc148901e86624a4d..468caf403742c73cf76c3b51db6ee849786f052d 100644 (file)
@@ -19,7 +19,7 @@ begin
     for ln in execute $1
     loop
         -- 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
         ln := regexp_replace(ln, '\m\d+kB', 'NkB', 'g');
         -- Ignore text-mode buffers output because it varies depending
@@ -103,3 +103,6 @@ select jsonb_pretty(
 );
 
 rollback;
+
+set compute_query_id = on;
+select explain_filter('explain (verbose) select 1');