v6-0001-Added-plan_id-to-the-core.patch
text/x-diff
Filename: v6-0001-Added-plan_id-to-the-core.patch
Type: text/x-diff
Part: 2
From 49518713775c3d24ba8e7a802c817042143e548a Mon Sep 17 00:00:00 2001
From: Andrey Kazachkov <andrey.kazachkov@tantorlabs.ru>
Date: Tue, 16 Dec 2025 19:52:39 +0300
Subject: [PATCH v6 1/2] Added plan_id to the core.
1. Changed a jumbling initialization routine
2. JumbleNode() is an entry point for jumbling
3. AppendJumble() remains static
4. guc-compute-plan-id has been added
5. gen_node_support.pl changed to process plan node structures
6. pg_node_attr has been added to certain structures/fields
---
doc/src/sgml/config.sgml | 34 ++++
doc/src/sgml/monitoring.sgml | 16 ++
src/backend/catalog/system_views.sql | 1 +
src/backend/commands/explain.c | 17 ++
src/backend/executor/execMain.c | 10 +-
src/backend/nodes/gen_node_support.pl | 34 +++-
src/backend/nodes/queryjumblefuncs.c | 180 +++++++++++-------
src/backend/optimizer/plan/planner.c | 19 ++
src/backend/optimizer/plan/setrefs.c | 9 +
src/backend/postmaster/launch_backend.c | 3 +
src/backend/utils/adt/pgstatfuncs.c | 7 +-
src/backend/utils/misc/guc_parameters.dat | 7 +
src/backend/utils/misc/guc_tables.c | 18 ++
src/backend/utils/misc/postgresql.conf.sample | 1 +
src/include/catalog/pg_proc.dat | 6 +-
src/include/nodes/pathnodes.h | 3 +
src/include/nodes/plannodes.h | 50 ++---
src/include/nodes/primnodes.h | 4 +-
src/include/nodes/queryjumble.h | 35 +++-
src/include/utils/backend_status.h | 2 +-
src/test/regress/expected/explain.out | 11 ++
src/test/regress/expected/rules.out | 9 +-
src/test/regress/sql/explain.sql | 4 +
23 files changed, 371 insertions(+), 109 deletions(-)
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 405c9689bd0..4cdaf9c49ba 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -8864,6 +8864,40 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
</listitem>
</varlistentry>
+ <varlistentry id="guc-compute-plan-id" xreflabel="compute_plan_id">
+ <term><varname>compute_plan_id</varname> (<type>enum</type>)
+ <indexterm>
+ <primary><varname>compute_plan_id</varname> configuration parameter</primary>
+ </indexterm>
+ </term>
+ <listitem>
+ <para>
+ Enables in-core computation of a plan identifier.
+ Plan identifiers can be displayed in the <link
+ linkend="monitoring-pg-stat-activity-view"><structname>pg_stat_activity</structname></link>
+ view or using <command>EXPLAIN</command>.
+ Note that an external module can alternatively be used if the
+ in-core plan identifier computation method is not acceptable.
+ In this case, in-core computation must be always disabled.
+ Valid values are <literal>off</literal> (always disabled),
+ <literal>on</literal> (always enabled), <literal>auto</literal>,
+ which lets modules that utilize plan identifiers enable
+ it automatically, and <literal>regress</literal> which
+ has the same effect as <literal>on</literal>, except that the
+ query identifier is not shown in the <literal>EXPLAIN</literal> output
+ in order to facilitate automated regression testing.
+ The default is <literal>auto</literal>.
+ </para>
+ <note>
+ <para>
+ To ensure that only one plan identifier is calculated and
+ displayed, extensions that calculate plan identifiers should
+ throw an error if a plan identifier has already been computed.
+ </para>
+ </note>
+ </listitem>
+ </varlistentry>
+
<varlistentry id="guc-log-statement-stats">
<term><varname>log_statement_stats</varname> (<type>boolean</type>)
<indexterm>
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 817fd9f4ca7..9e98fd2c198 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -983,6 +983,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>plan_id</structfield> <type>bigint</type>
+ </para>
+ <para>
+ Identifier of this backend's most recent query plan. If
+ <structfield>state</structfield> is <literal>active</literal> this
+ field shows the identifier of the currently executing query plan. In
+ all other states, it shows the identifier of last query plan that
+ was executed. Plan identifiers are not computed by default so this
+ field will be null unless <xref linkend="guc-compute-plan-id"/>
+ parameter is enabled or a third-party module that computes plan
+ identifiers is configured.
+ </para></entry>
+ </row>
+
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>query</structfield> <type>text</type>
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 0a0f95f6bb9..35744a82f0b 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -919,6 +919,7 @@ CREATE VIEW pg_stat_activity AS
S.backend_xid,
S.backend_xmin,
S.query_id,
+ S.plan_id,
S.query,
S.backend_type
FROM pg_stat_get_activity(NULL) AS S
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index 5a6390631eb..b21cd142111 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -818,6 +818,23 @@ ExplainPrintPlan(ExplainState *es, QueryDesc *queryDesc)
ExplainPropertyInteger("Query Identifier", NULL,
queryDesc->plannedstmt->queryId, es);
}
+
+ /*
+ * COMPUTE_PLAN_ID_REGRESS means COMPUTE_PLAN_ID_YES, but we don't show
+ * the queryid in any of the EXPLAIN plans to keep stable the results
+ * generated by regression test suites.
+ */
+ if (es->verbose && queryDesc->plannedstmt->planId != UINT64CONST(0) &&
+ compute_plan_id != COMPUTE_PLAN_ID_REGRESS)
+ {
+ /*
+ * Output the queryid as an int64 rather than a uint64 so we match
+ * what would be seen in the BIGINT pg_stat_activity.plan_id column.
+ */
+ ExplainPropertyInteger("Plan Identifier", NULL,
+ queryDesc->plannedstmt->planId, es);
+ }
+
}
/*
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 797d8b1ca1c..14f107d45b1 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -123,13 +123,15 @@ ExecutorStart(QueryDesc *queryDesc, int eflags)
{
/*
* In some cases (e.g. an EXECUTE statement or an execute message with the
- * extended query protocol) the query_id won't be reported, so do it now.
+ * extended query protocol) the query_id and plan_id won't be reported, so
+ * do it now.
*
- * Note that it's harmless to report the query_id multiple times, as the
- * call will be ignored if the top level query_id has already been
- * reported.
+ * Note that it's harmless to report the identifiers multiple times, as
+ * the call will be ignored if the top level query_id or plan_id has
+ * already been reported.
*/
pgstat_report_query_id(queryDesc->plannedstmt->queryId, false);
+ pgstat_report_plan_id(queryDesc->plannedstmt->planId, false);
if (ExecutorStart_hook)
(*ExecutorStart_hook) (queryDesc, eflags);
diff --git a/src/backend/nodes/gen_node_support.pl b/src/backend/nodes/gen_node_support.pl
index 9ecddb14231..eab144bd962 100644
--- a/src/backend/nodes/gen_node_support.pl
+++ b/src/backend/nodes/gen_node_support.pl
@@ -1291,6 +1291,7 @@ _jumble${n}(JumbleState *jstate, Node *node)
{
my $t = $node_type_info{$n}->{field_types}{$f};
my @a = @{ $node_type_info{$n}->{field_attrs}{$f} };
+ my $array_size_field;
my $query_jumble_ignore = $struct_no_query_jumble;
my $query_jumble_custom = 0;
my $query_jumble_location = 0;
@@ -1303,7 +1304,11 @@ _jumble${n}(JumbleState *jstate, Node *node)
{
$query_jumble_custom = 1;
}
- if ($a eq 'query_jumble_ignore')
+ elsif ($a =~ /^array_size\(([\w.]+)\)$/)
+ {
+ $array_size_field = $1;
+ }
+ elsif ($a eq 'query_jumble_ignore')
{
$query_jumble_ignore = 1;
}
@@ -1317,12 +1322,19 @@ _jumble${n}(JumbleState *jstate, Node *node)
}
}
+ next if $query_jumble_ignore;
+
if ($query_jumble_custom)
{
# Custom function that applies to one field of a node.
print $jff "\tJUMBLE_CUSTOM($n, $f);\n"
unless $query_jumble_ignore;
}
+ elsif ($t eq 'Bitmapset*')
+ {
+ print $jff "\tJUMBLE_BITMAPSET($f);\n"
+ unless $query_jumble_ignore;
+ }
elsif (($t =~ /^(\w+)\*$/ or $t =~ /^struct\s+(\w+)\*$/)
and elem $1, @node_types)
{
@@ -1352,6 +1364,26 @@ _jumble${n}(JumbleState *jstate, Node *node)
print $jff "\tJUMBLE_STRING($f);\n"
unless $query_jumble_ignore;
}
+ elsif ($t =~ /^(\w+)(\*|\[\w+\])$/ and elem $1, @scalar_types)
+ {
+ if (!defined $array_size_field)
+ {
+ die "no array size defined for $n.$f of type $t\n";
+ }
+ if ($node_type_info{$n}->{field_types}{$array_size_field} eq
+ 'List*')
+ {
+ print $jff
+ "\tJUMBLE_ARRAY($f, list_length(expr->$array_size_field));\n"
+ unless $query_jumble_ignore;
+ }
+ else
+ {
+ print $jff
+ "\tJUMBLE_ARRAY($f, expr->$array_size_field);\n"
+ unless $query_jumble_ignore;
+ }
+ }
else
{
print $jff "\tJUMBLE_FIELD($f);\n"
diff --git a/src/backend/nodes/queryjumblefuncs.c b/src/backend/nodes/queryjumblefuncs.c
index ffc230af427..5717480dbf5 100644
--- a/src/backend/nodes/queryjumblefuncs.c
+++ b/src/backend/nodes/queryjumblefuncs.c
@@ -3,32 +3,31 @@
* queryjumblefuncs.c
* Query normalization and fingerprinting.
*
- * Normalization is a process whereby similar queries, typically differing only
- * in their constants (though the exact rules are somewhat more subtle than
- * that) are recognized as equivalent, and are tracked as a single entry. This
- * is particularly useful for non-prepared queries.
+ * Fingerprinting selectively serializes key fields within a tree structure,
+ * such as a Query or Plan tree, to create a unique identifier while ignoring
+ * extraneous details. These essential fields are concatenated into a jumble,
+ * from which a 64-bit hash is computed. Unlike regular serialization, this
+ * approach excludes irrelevant information.
+ *
+ * Use Cases:
*
- * Normalization is implemented by fingerprinting queries, selectively
- * serializing those fields of each query tree's nodes that are judged to be
- * essential to the query. This is referred to as a query jumble. This is
- * distinct from a regular serialization in that various extraneous
- * information is ignored as irrelevant or not essential to the query, such
- * as the collations of Vars and, most notably, the values of constants.
- *
- * This jumble is acquired at the end of parse analysis of each query, and
- * a 64-bit hash of it is stored into the query's Query.queryId field.
- * The server then copies this value around, making it available in plan
- * tree(s) generated from the query. The executor can then use this value
- * to blame query costs on the proper queryId.
- *
- * Arrays of two or more constants and PARAM_EXTERN parameters are "squashed"
- * and contribute only once to the jumble. This has the effect that queries
- * that differ only on the length of such lists have the same queryId.
- *
- *
- * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
+ * 1. In-Core Query Normalization & Identification
*
+ * Fingerprinting is used to normalize query trees by generating a hash stored
+ * in the Query.queryId field. This ID is propagated to plan tree(s), allowing
+ * the executor to attribute query costs on the proper queryId. The process
+ * excludes information like typmod, collation, and most notably, the values
+ * of constants.
+ *
+ * Example: The following queries produce the same queryId:
+ *
+ * SELECT t.* FROM s1.t WHERE c1 = 1;
+ * SELECT t.* FROM s1.t WHERE c1 = 2;
+ *
+ * 2. Modified jumbling logic for extensions
+ *
+ * Extensions can modify the fingerprinting logic for queryId, or fingerprint
+ * other types of trees, such as a plan tree, to compute a plan identifier.
*
* IDENTIFICATION
* src/backend/nodes/queryjumblefuncs.c
@@ -50,6 +49,7 @@
/* GUC parameters */
int compute_query_id = COMPUTE_QUERY_ID_AUTO;
+int compute_plan_id = COMPUTE_PLAN_ID_AUTO;
/*
* True when compute_query_id is ON or AUTO, and a module requests them.
@@ -60,15 +60,21 @@ int compute_query_id = COMPUTE_QUERY_ID_AUTO;
*/
bool query_id_enabled = false;
-static JumbleState *InitJumble(void);
-static int64 DoJumble(JumbleState *jstate, Node *node);
+/*
+ * True when compute_plan_id is ON or AUTO, and a module requests them.
+ *
+ * Note that IsPlanIdEnabled() should be used instead of checking
+ * plan_id_enabled or compute_plan_id directly when we want to know
+ * whether query identifiers are computed in the core or not.
+ */
+bool plan_id_enabled = false;
+
static void AppendJumble(JumbleState *jstate,
const unsigned char *value, Size size);
static void FlushPendingNulls(JumbleState *jstate);
static void RecordConstLocation(JumbleState *jstate,
bool extern_param,
int location, int len);
-static void _jumbleNode(JumbleState *jstate, Node *node);
static void _jumbleList(JumbleState *jstate, Node *node);
static void _jumbleElements(JumbleState *jstate, List *elements, Node *node);
static void _jumbleParam(JumbleState *jstate, Node *node);
@@ -136,13 +142,13 @@ CleanQuerytext(const char *query, int *location, int *len)
JumbleState *
JumbleQuery(Query *query)
{
- JumbleState *jstate;
+ JumbleState *jstate = InitializeJumbleState(true);;
Assert(IsQueryIdEnabled());
- jstate = InitJumble();
-
- query->queryId = DoJumble(jstate, (Node *) query);
+ /* Compute query ID and mark the Query node with it */
+ JumbleNode(jstate, (Node *) query);
+ query->queryId = HashJumbleState(jstate);
/*
* If we are unlucky enough to get a hash of zero, use 1 instead for
@@ -173,44 +179,44 @@ EnableQueryId(void)
}
/*
- * InitJumble
- * Allocate a JumbleState object and make it ready to jumble.
+ * Enables plan identifier computation.
+ *
+ * Third-party plugins can use this function to inform core that they require
+ * a query identifier to be computed.
*/
-static JumbleState *
-InitJumble(void)
+void
+EnablePlanId(void)
{
- JumbleState *jstate;
+ if (compute_plan_id != COMPUTE_PLAN_ID_OFF)
+ plan_id_enabled = true;
+}
- jstate = palloc_object(JumbleState);
+/*
+ * InitializeJumbleState
+ * Allocate a JumbleState object and make it ready to jumble.
+ */
+JumbleState *
+InitializeJumbleState(bool record_clocations)
+{
+ JumbleState *jstate = (JumbleState *) palloc0(sizeof(JumbleState));
/* Set up workspace for query jumbling */
jstate->jumble = (unsigned char *) palloc(JUMBLE_SIZE);
jstate->jumble_len = 0;
- jstate->clocations_buf_size = 32;
- jstate->clocations = (LocationLen *) palloc(jstate->clocations_buf_size *
- sizeof(LocationLen));
- jstate->clocations_count = 0;
- jstate->highest_extern_param_id = 0;
- jstate->pending_nulls = 0;
- jstate->has_squashed_lists = false;
-#ifdef USE_ASSERT_CHECKING
- jstate->total_jumble_len = 0;
-#endif
+
+ if (record_clocations)
+ {
+ jstate->clocations_buf_size = 32;
+ jstate->clocations = (LocationLen *)
+ palloc(jstate->clocations_buf_size * sizeof(LocationLen));
+ }
return jstate;
}
-/*
- * DoJumble
- * Jumble the given Node using the given JumbleState and return the resulting
- * jumble hash.
- */
-static int64
-DoJumble(JumbleState *jstate, Node *node)
+uint64
+HashJumbleState(JumbleState *jstate)
{
- /* Jumble the given node */
- _jumbleNode(jstate, node);
-
/* Flush any pending NULLs before doing the final hash */
if (jstate->pending_nulls > 0)
FlushPendingNulls(jstate);
@@ -219,10 +225,9 @@ DoJumble(JumbleState *jstate, Node *node)
if (jstate->has_squashed_lists)
jstate->highest_extern_param_id = 0;
- /* Process the jumble buffer and produce the hash value */
- return DatumGetInt64(hash_any_extended(jstate->jumble,
- jstate->jumble_len,
- 0));
+ return DatumGetUInt64(hash_any_extended(jstate->jumble,
+ jstate->jumble_len,
+ 0));
}
/*
@@ -398,7 +403,7 @@ static void
RecordConstLocation(JumbleState *jstate, bool extern_param, int location, int len)
{
/* -1 indicates unknown or undefined location */
- if (location >= 0)
+ if (location >= 0 && jstate->clocations_buf_size > 0)
{
/* enlarge array if needed */
if (jstate->clocations_count >= jstate->clocations_buf_size)
@@ -526,7 +531,7 @@ IsSquashableConstantList(List *elements)
}
#define JUMBLE_NODE(item) \
- _jumbleNode(jstate, (Node *) expr->item)
+ JumbleNode(jstate, (Node *) expr->item)
#define JUMBLE_ELEMENTS(list, node) \
_jumbleElements(jstate, (List *) expr->list, node)
#define JUMBLE_LOCATION(location) \
@@ -544,6 +549,13 @@ do { \
else \
AppendJumble(jstate, (const unsigned char *) &(expr->item), sizeof(expr->item)); \
} while (0)
+#define JUMBLE_BITMAPSET(item) \
+do { \
+ if (expr->item) \
+ AppendJumble(jstate, (const unsigned char *) expr->item->words, sizeof(bitmapword) * expr->item->nwords); \
+} while(0)
+#define JUMBLE_ARRAY(item, len) \
+ AppendJumble(jstate, (const unsigned char *) expr->item, sizeof(*(expr->item)) * len)
#define JUMBLE_STRING(str) \
do { \
if (expr->str) \
@@ -557,8 +569,8 @@ do { \
#include "queryjumblefuncs.funcs.c"
-static void
-_jumbleNode(JumbleState *jstate, Node *node)
+void
+JumbleNode(JumbleState *jstate, Node *node)
{
Node *expr = node;
#ifdef USE_ASSERT_CHECKING
@@ -612,7 +624,7 @@ _jumbleList(JumbleState *jstate, Node *node)
{
case T_List:
foreach(l, expr)
- _jumbleNode(jstate, lfirst(l));
+ JumbleNode(jstate, lfirst(l));
break;
case T_IntList:
foreach(l, expr)
@@ -668,7 +680,7 @@ _jumbleElements(JumbleState *jstate, List *elements, Node *node)
if (!normalize_list)
{
- _jumbleNode(jstate, (Node *) elements);
+ JumbleNode(jstate, (Node *) elements);
}
}
@@ -758,6 +770,40 @@ _jumbleVariableSetStmt(JumbleState *jstate, Node *node)
JUMBLE_LOCATION(location);
}
+/*
+ * Jumble the entries in the rangle table to map RT indexes to relations
+ *
+ * This ensures jumbled RT indexes (e.g. in a Scan or Modify node), are
+ * distinguished by the target of the RT entry, even if the index is the same.
+ */
+void
+JumbleRangeTable(JumbleState *jstate, List *rtable)
+{
+ ListCell *lc;
+
+ foreach(lc, rtable)
+ {
+ RangeTblEntry *expr = lfirst_node(RangeTblEntry, lc);
+
+ switch (expr->rtekind)
+ {
+ case RTE_RELATION:
+ JUMBLE_FIELD(relid);
+ break;
+ case RTE_CTE:
+ JUMBLE_STRING(ctename);
+ break;
+ default:
+
+ /*
+ * Ignore other targets, the jumble includes something identifying
+ * about them already
+ */
+ break;
+ }
+ }
+}
+
/*
* Custom query jumble function for RangeTblEntry.eref.
*/
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 8b22c30559b..0309566f602 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -37,6 +37,7 @@
#ifdef OPTIMIZER_DEBUG
#include "nodes/print.h"
#endif
+#include "nodes/queryjumble.h"
#include "nodes/supportnodes.h"
#include "optimizer/appendinfo.h"
#include "optimizer/clauses.h"
@@ -578,6 +579,16 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions,
Assert(glob->finalrowmarks == NIL);
Assert(glob->resultRelations == NIL);
Assert(glob->appendRelations == NIL);
+
+ /*
+ * Initialize plan identifier jumble if needed
+ *
+ * Note the actual jumbling is done in the tree walk in
+ * set_plan_references
+ */
+ if (IsPlanIdEnabled())
+ glob->plan_jumble_state = InitializeJumbleState(false);
+
top_plan = set_plan_references(root, top_plan);
/* ... and the subplans (both regular subplans and initplans) */
Assert(list_length(glob->subplans) == list_length(glob->subroots));
@@ -645,6 +656,14 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions,
result->jitFlags |= PGJIT_DEFORM;
}
+ if (IsPlanIdEnabled())
+ {
+ JumbleRangeTable(glob->plan_jumble_state, glob->finalrtable);
+ result->planId = HashJumbleState(glob->plan_jumble_state);
+ pfree(glob->plan_jumble_state->jumble);
+ pfree(glob->plan_jumble_state);
+ }
+
/* Allow plugins to take control before we discard "glob" */
if (planner_shutdown_hook)
(*planner_shutdown_hook) (glob, parse, query_string, result);
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index cd7ea1e6b58..947bd980c66 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -19,6 +19,7 @@
#include "catalog/pg_type.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
+#include "nodes/queryjumble.h"
#include "optimizer/optimizer.h"
#include "optimizer/pathnode.h"
#include "optimizer/planmain.h"
@@ -1338,6 +1339,14 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
plan->lefttree = set_plan_refs(root, plan->lefttree, rtoffset);
plan->righttree = set_plan_refs(root, plan->righttree, rtoffset);
+ /*
+ * If enabled, append significant information to the plan identifier
+ * jumble (we do this here since we're already walking the tree in a
+ * near-final state)
+ */
+ if (IsPlanIdEnabled())
+ JumbleNode(root->glob->plan_jumble_state, (Node *) plan);
+
return plan;
}
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 98f7c4848c9..b64c88db200 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -118,6 +118,7 @@ typedef struct
bool redirection_done;
bool IsBinaryUpgrade;
bool query_id_enabled;
+ bool plan_id_enabled;
int max_safe_fds;
int MaxBackends;
int num_pmchild_slots;
@@ -757,6 +758,7 @@ save_backend_variables(BackendParameters *param,
param->redirection_done = redirection_done;
param->IsBinaryUpgrade = IsBinaryUpgrade;
param->query_id_enabled = query_id_enabled;
+ param->plan_id_enabled = plan_id_enabled;
param->max_safe_fds = max_safe_fds;
param->MaxBackends = MaxBackends;
@@ -1019,6 +1021,7 @@ restore_backend_variables(BackendParameters *param)
redirection_done = param->redirection_done;
IsBinaryUpgrade = param->IsBinaryUpgrade;
query_id_enabled = param->query_id_enabled;
+ plan_id_enabled = param->plan_id_enabled;
max_safe_fds = param->max_safe_fds;
MaxBackends = param->MaxBackends;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index ef6fffe60b9..854c9f8bffd 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -351,7 +351,7 @@ pg_stat_get_progress_info(PG_FUNCTION_ARGS)
Datum
pg_stat_get_activity(PG_FUNCTION_ARGS)
{
-#define PG_STAT_GET_ACTIVITY_COLS 31
+#define PG_STAT_GET_ACTIVITY_COLS 32
int num_backends = pgstat_fetch_stat_numbackends();
int curr_backend;
int pid = PG_ARGISNULL(0) ? -1 : PG_GETARG_INT32(0);
@@ -665,6 +665,10 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
nulls[30] = true;
else
values[30] = Int64GetDatum(beentry->st_query_id);
+ if (beentry->st_plan_id == INT64CONST(0))
+ nulls[31] = true;
+ else
+ values[31] = UInt64GetDatum(beentry->st_plan_id);
}
else
{
@@ -694,6 +698,7 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
nulls[28] = true;
nulls[29] = true;
nulls[30] = true;
+ nulls[31] = true;
}
tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
diff --git a/src/backend/utils/misc/guc_parameters.dat b/src/backend/utils/misc/guc_parameters.dat
index 3b9d8349078..d9f5cf31c34 100644
--- a/src/backend/utils/misc/guc_parameters.dat
+++ b/src/backend/utils/misc/guc_parameters.dat
@@ -462,6 +462,13 @@
check_hook => 'check_commit_ts_buffers',
},
+{ name => 'compute_plan_id', type => 'enum', context => 'PGC_SUSET', group => 'STATS_MONITORING',
+ short_desc => 'Enables in-core computation of plan identifiers.',
+ variable => 'compute_plan_id',
+ boot_val => 'COMPUTE_PLAN_ID_AUTO',
+ options => 'compute_plan_id_options',
+},
+
{ name => 'compute_query_id', type => 'enum', context => 'PGC_SUSET', group => 'STATS_MONITORING',
short_desc => 'Enables in-core computation of query identifiers.',
variable => 'compute_query_id',
diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c
index f87b558c2c6..3a5d7ee32ab 100644
--- a/src/backend/utils/misc/guc_tables.c
+++ b/src/backend/utils/misc/guc_tables.c
@@ -318,6 +318,24 @@ static const struct config_enum_entry compute_query_id_options[] = {
{NULL, 0, false}
};
+/*
+ * Although only "on" and "off" are documented, we accept
+ * all the likely variants of "on" and "off".
+ */
+static const struct config_enum_entry compute_plan_id_options[] = {
+ {"auto", COMPUTE_PLAN_ID_AUTO, false},
+ {"regress", COMPUTE_PLAN_ID_REGRESS, false},
+ {"on", COMPUTE_PLAN_ID_ON, false},
+ {"off", COMPUTE_PLAN_ID_OFF, false},
+ {"true", COMPUTE_PLAN_ID_ON, true},
+ {"false", COMPUTE_PLAN_ID_OFF, true},
+ {"yes", COMPUTE_PLAN_ID_ON, true},
+ {"no", COMPUTE_PLAN_ID_OFF, true},
+ {"1", COMPUTE_PLAN_ID_ON, true},
+ {"0", COMPUTE_PLAN_ID_OFF, true},
+ {NULL, 0, false}
+};
+
/*
* Although only "on", "off", and "partition" are documented, we
* accept all the likely variants of "on" and "off".
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index dc9e2255f8a..3d0e42f083b 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -674,6 +674,7 @@
# - Monitoring -
#compute_query_id = auto
+#compute_plan_id = auto
#log_statement_stats = off
#log_parser_stats = off
#log_planner_stats = off
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index fd9448ec7b9..2bf284e2732 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5653,9 +5653,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,text,numeric,text,bool,text,bool,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,ssl_client_dn,ssl_client_serial,ssl_issuer_dn,gss_auth,gss_princ,gss_enc,gss_delegation,leader_pid,query_id}',
+ 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,bool,int4,int8,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,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,gss_delegation,leader_pid,query_id,plan_id}',
prosrc => 'pg_stat_get_activity' },
{ oid => '6318', descr => 'describe wait events',
proname => 'pg_get_wait_events', procost => '10', prorows => '250',
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index b5ff456ef7f..c98f7b97283 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -189,6 +189,9 @@ typedef struct PlannerGlobal
/* extension state */
void **extension_state pg_node_attr(read_write_ignore);
int extension_state_allocated;
+
+ /* optional jumble state for plan identifier calculation */
+ struct JumbleState *plan_jumble_state pg_node_attr(read_write_ignore);
} PlannerGlobal;
/* macro for fetching the Plan associated with a SubPlan node */
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index c4393a94321..a5a2ccea1f7 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -70,7 +70,7 @@ typedef struct PlannedStmt
/* query identifier (copied from Query) */
int64 queryId;
- /* plan identifier (can be set by plugins) */
+ /* plan identifier (set when compute_plan_id is enabled or by plugins) */
int64 planId;
/* origin of plan */
@@ -184,7 +184,7 @@ typedef struct PlannedStmt
*/
typedef struct Plan
{
- pg_node_attr(abstract, no_equal, no_query_jumble)
+ pg_node_attr(abstract, no_equal)
NodeTag type;
@@ -192,19 +192,19 @@ typedef struct Plan
* estimated execution costs for plan (see costsize.c for more info)
*/
/* count of disabled nodes */
- int disabled_nodes;
+ int disabled_nodes pg_node_attr(query_jumble_ignore);
/* cost expended before fetching any tuples */
- Cost startup_cost;
+ Cost startup_cost pg_node_attr(query_jumble_ignore);
/* total cost (assuming all tuples fetched) */
- Cost total_cost;
+ Cost total_cost pg_node_attr(query_jumble_ignore);
/*
* planner's estimate of result size of this plan step
*/
/* number of rows plan is expected to emit */
- Cardinality plan_rows;
+ Cardinality plan_rows pg_node_attr(query_jumble_ignore);
/* average row width in bytes */
- int plan_width;
+ int plan_width pg_node_attr(query_jumble_ignore);
/*
* information needed for parallel query
@@ -230,10 +230,10 @@ typedef struct Plan
/* implicitly-ANDed qual conditions */
List *qual;
/* input plan tree(s) */
- struct Plan *lefttree;
- struct Plan *righttree;
+ struct Plan *lefttree pg_node_attr(query_jumble_ignore);
+ struct Plan *righttree pg_node_attr(query_jumble_ignore);
/* Init Plan nodes (un-correlated expr subselects) */
- List *initPlan;
+ List *initPlan pg_node_attr(query_jumble_ignore);
/*
* Information for management of parameter-change-driven rescanning
@@ -351,7 +351,7 @@ typedef struct ModifyTable
/* per-target-table RETURNING tlists */
List *returningLists;
/* per-target-table FDW private data lists */
- List *fdwPrivLists;
+ List *fdwPrivLists pg_node_attr(query_jumble_ignore);
/* indices of FDW DM plans */
Bitmapset *fdwDirectModifyPlans;
/* PlanRowMarks (non-locking only) */
@@ -390,7 +390,7 @@ typedef struct Append
Plan plan;
/* RTIs of appendrel(s) formed by this node */
Bitmapset *apprelids;
- List *appendplans;
+ List *appendplans pg_node_attr(query_jumble_ignore);
/* # of asynchronous plans */
int nasyncplans;
@@ -420,7 +420,7 @@ typedef struct MergeAppend
/* RTIs of appendrel(s) formed by this node */
Bitmapset *apprelids;
- List *mergeplans;
+ List *mergeplans pg_node_attr(query_jumble_ignore);
/* these fields are just like the sort-key info in struct Sort: */
@@ -475,7 +475,7 @@ typedef struct RecursiveUnion
Oid *dupCollations pg_node_attr(array_size(numCols));
/* estimated number of groups in input */
- Cardinality numGroups;
+ long numGroups pg_node_attr(query_jumble_ignore);
} RecursiveUnion;
/* ----------------
@@ -489,7 +489,7 @@ typedef struct RecursiveUnion
typedef struct BitmapAnd
{
Plan plan;
- List *bitmapplans;
+ List *bitmapplans pg_node_attr(query_jumble_ignore);
} BitmapAnd;
/* ----------------
@@ -504,7 +504,7 @@ typedef struct BitmapOr
{
Plan plan;
bool isshared;
- List *bitmapplans;
+ List *bitmapplans pg_node_attr(query_jumble_ignore);
} BitmapOr;
/*
@@ -752,7 +752,7 @@ typedef enum SubqueryScanStatus
typedef struct SubqueryScan
{
Scan scan;
- Plan *subplan;
+ Plan *subplan pg_node_attr(query_jumble_ignore);
SubqueryScanStatus scanstatus;
} SubqueryScan;
@@ -880,7 +880,7 @@ typedef struct ForeignScan
/* expressions that FDW may evaluate */
List *fdw_exprs;
/* private data for FDW */
- List *fdw_private;
+ List *fdw_private pg_node_attr(query_jumble_ignore);
/* optional tlist describing scan tuple */
List *fdw_scan_tlist;
/* original quals not in scan.plan.qual */
@@ -918,7 +918,7 @@ typedef struct CustomScan
/* expressions that custom code may evaluate */
List *custom_exprs;
/* private data for custom code */
- List *custom_private;
+ List *custom_private pg_node_attr(query_jumble_ignore);
/* optional tlist describing scan tuple */
List *custom_scan_tlist;
/* RTIs generated by this scan */
@@ -929,7 +929,7 @@ typedef struct CustomScan
* static table of callback functions. So we don't copy the table itself,
* just reference the original one.
*/
- const struct CustomScanMethods *methods;
+ const struct CustomScanMethods *methods pg_node_attr(query_jumble_ignore);
} CustomScan;
/*
@@ -991,7 +991,7 @@ typedef struct NestLoop
typedef struct NestLoopParam
{
- pg_node_attr(no_equal, no_query_jumble)
+ pg_node_attr(no_equal)
NodeTag type;
/* number of the PARAM_EXEC Param to set */
@@ -1099,7 +1099,7 @@ typedef struct Memoize
* The maximum number of entries that the planner expects will fit in the
* cache, or 0 if unknown
*/
- uint32 est_entries;
+ uint32 est_entries pg_node_attr(query_jumble_ignore);
/* paramids from param_exprs */
Bitmapset *keyparamids;
@@ -1206,7 +1206,7 @@ typedef struct Agg
Oid *grpCollations pg_node_attr(array_size(numCols));
/* estimated number of groups in input */
- Cardinality numGroups;
+ long numGroups pg_node_attr(query_jumble_ignore);
/* for pass-by-ref transition data */
uint64 transitionSpace;
@@ -1415,7 +1415,7 @@ typedef struct Hash
bool skewInherit;
/* all other info is in the parent HashJoin node */
/* estimate total rows if parallel_aware */
- Cardinality rows_total;
+ Cardinality rows_total pg_node_attr(query_jumble_ignore);
} Hash;
/* ----------------
@@ -1446,7 +1446,7 @@ typedef struct SetOp
bool *cmpNullsFirst pg_node_attr(array_size(numCols));
/* estimated number of groups in left input */
- Cardinality numGroups;
+ long numGroups pg_node_attr(query_jumble_ignore);
} SetOp;
/* ----------------
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 1b4436f2ff6..4ccba6bcd03 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -1123,8 +1123,8 @@ typedef struct SubPlan
List *parParam; /* indices of input Params from parent plan */
List *args; /* exprs to pass as parParam values */
/* Estimated execution costs: */
- Cost startup_cost; /* one-time setup cost */
- Cost per_call_cost; /* cost for each subplan evaluation */
+ Cost startup_cost pg_node_attr(query_jumble_ignore); /* one-time setup cost */
+ Cost per_call_cost pg_node_attr(query_jumble_ignore); /* cost for each subplan evaluation */
} SubPlan;
/*
diff --git a/src/include/nodes/queryjumble.h b/src/include/nodes/queryjumble.h
index dcb36dcb44f..aeb20aa0587 100644
--- a/src/include/nodes/queryjumble.h
+++ b/src/include/nodes/queryjumble.h
@@ -15,6 +15,7 @@
#define QUERYJUMBLE_H
#include "nodes/parsenodes.h"
+#include "nodes/pathnodes.h"
/*
* Struct for tracking locations/lengths of constants during normalization
@@ -86,15 +87,26 @@ enum ComputeQueryIdType
COMPUTE_QUERY_ID_REGRESS,
};
+/* Values for the compute_plan_id GUC */
+enum ComputePlanIdType
+{
+ COMPUTE_PLAN_ID_OFF,
+ COMPUTE_PLAN_ID_ON,
+ COMPUTE_PLAN_ID_AUTO,
+ COMPUTE_PLAN_ID_REGRESS,
+};
+
/* GUC parameters */
extern PGDLLIMPORT int compute_query_id;
-
+extern PGDLLIMPORT int compute_plan_id;
extern const char *CleanQuerytext(const char *query, int *location, int *len);
extern JumbleState *JumbleQuery(Query *query);
extern void EnableQueryId(void);
+extern void EnablePlanId(void);
extern PGDLLIMPORT bool query_id_enabled;
+extern PGDLLIMPORT bool plan_id_enabled;
/*
* Returns whether query identifier computation has been enabled, either
@@ -110,4 +122,25 @@ IsQueryIdEnabled(void)
return query_id_enabled;
}
+/*
+ * Returns whether plan identifier computation has been enabled, either
+ * directly in the GUC or by a module when the setting is 'auto'.
+ */
+static inline bool
+IsPlanIdEnabled(void)
+{
+ if (compute_plan_id == COMPUTE_PLAN_ID_OFF)
+ return false;
+ if (compute_plan_id == COMPUTE_PLAN_ID_ON)
+ return true;
+ return plan_id_enabled;
+}
+
+/* Functions called for plan jumbling or extensions doing their own jumbling */
+extern JumbleState *InitializeJumbleState(bool record_clocations);
+//extern void AppendJumble(JumbleState *jstate, const unsigned char *item, Size size);
+extern void JumbleRangeTable(JumbleState *jstate, List *rtable);
+extern void JumbleNode(JumbleState *jstate, Node *node);
+extern uint64 HashJumbleState(JumbleState *jstate);
+
#endif /* QUERYJUMBLE_H */
diff --git a/src/include/utils/backend_status.h b/src/include/utils/backend_status.h
index 3016501ac05..45f00eb93a8 100644
--- a/src/include/utils/backend_status.h
+++ b/src/include/utils/backend_status.h
@@ -172,7 +172,7 @@ typedef struct PgBackendStatus
/* query identifier, optionally computed using post_parse_analyze_hook */
int64 st_query_id;
- /* plan identifier, optionally computed using planner_hook */
+ /* plan identifier, optionally computed after planning */
int64 st_plan_id;
} PgBackendStatus;
diff --git a/src/test/regress/expected/explain.out b/src/test/regress/expected/explain.out
index 7c1f26b182c..7ac025cca84 100644
--- a/src/test/regress/expected/explain.out
+++ b/src/test/regress/expected/explain.out
@@ -743,6 +743,17 @@ select explain_filter('explain (verbose) create table test_ctas as select 1');
Query Identifier: N
(3 rows)
+-- Test compute_plan_id
+set compute_plan_id = on;
+select explain_filter('explain (verbose) select * from int8_tbl i8');
+ explain_filter
+----------------------------------------------------------------
+ Seq Scan on public.int8_tbl i8 (cost=N.N..N.N rows=N width=N)
+ Output: q1, q2
+ Query Identifier: N
+ Plan Identifier: N
+(4 rows)
+
-- Test SERIALIZE option
select explain_filter('explain (analyze,buffers off,serialize) select * from int8_tbl i8');
explain_filter
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 4286c266e17..29af9bd2de9 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1794,9 +1794,10 @@ pg_stat_activity| SELECT s.datid,
s.backend_xid,
s.backend_xmin,
s.query_id,
+ s.plan_id,
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, gss_delegation, leader_pid, query_id)
+ 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, gss_delegation, leader_pid, query_id, plan_id)
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,
@@ -1926,7 +1927,7 @@ pg_stat_gssapi| SELECT pid,
gss_princ AS principal,
gss_enc AS encrypted,
gss_delegation AS credentials_delegated
- 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, gss_delegation, leader_pid, query_id)
+ 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, gss_delegation, leader_pid, query_id, plan_id)
WHERE (client_port IS NOT NULL);
pg_stat_io| SELECT backend_type,
object,
@@ -2156,7 +2157,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, gss_delegation, leader_pid, query_id)
+ 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, gss_delegation, leader_pid, query_id, plan_id)
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,
@@ -2193,7 +2194,7 @@ pg_stat_ssl| SELECT pid,
ssl_client_dn AS client_dn,
ssl_client_serial AS client_serial,
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, gss_delegation, leader_pid, query_id)
+ 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, gss_delegation, leader_pid, query_id, plan_id)
WHERE (client_port IS NOT NULL);
pg_stat_subscription| SELECT su.oid AS subid,
su.subname,
diff --git a/src/test/regress/sql/explain.sql b/src/test/regress/sql/explain.sql
index ebdab42604b..0449f05c1e1 100644
--- a/src/test/regress/sql/explain.sql
+++ b/src/test/regress/sql/explain.sql
@@ -173,6 +173,10 @@ select explain_filter('explain (verbose) select * from int8_tbl i8');
select explain_filter('explain (verbose) declare test_cur cursor for select * from int8_tbl');
select explain_filter('explain (verbose) create table test_ctas as select 1');
+-- Test compute_plan_id
+set compute_plan_id = on;
+select explain_filter('explain (verbose) select * from int8_tbl i8');
+
-- Test SERIALIZE option
select explain_filter('explain (analyze,buffers off,serialize) select * from int8_tbl i8');
select explain_filter('explain (analyze,serialize text,buffers,timing off) select * from int8_tbl i8');
base-commit: b39013b7b1b116b5d9be51f0919b472b58b3a28d
--
2.43.0