From df4a16463541a7b7013ceec029389d5da363b3e1 Mon Sep 17 00:00:00 2001
From: Robert Haas <rhaas@postgresql.org>
Date: Fri, 21 Feb 2025 11:48:32 -0500
Subject: [PATCH v1 2/3] Add some new hooks so extensions can add details to
 EXPLAIN.

Specifically, add a per-node hook that is called after the per-node
information has been displayed but before we display children, and a
per-query hook that is called after existing query-level information
is printed. This assumes that extension-added information should
always go at the end rather than the beginning or the middle, but
that seems like an acceptable limitation for simplicity. It also
assumes that extensions will only want to add information, not remove
or reformat existing details; those also seem like acceptable
restrictions, at least for now.

If multiple EXPLAIN extensions are used, the order in which any
additional details are printed is likely to depend on the order in
which the modules are loaded. That doesn't seem great; so possibly
we should add some kind of registration interface that also allows
extensions to specify a priority, so that we can print details in
a predictable order. However, that would be less similar to existing
hooks, and perhaps not worth the complexity, so do this for now.
---
 contrib/auto_explain/auto_explain.c  |  1 +
 contrib/file_fdw/file_fdw.c          |  1 +
 src/backend/commands/explain.c       | 13 +++++++++++++
 src/include/commands/explain.h       | 17 +++++++++++++++++
 src/include/commands/explain_state.h |  4 ++++
 5 files changed, 36 insertions(+)

diff --git a/contrib/auto_explain/auto_explain.c b/contrib/auto_explain/auto_explain.c
index 6b8a4f49443..704c45f1de3 100644
--- a/contrib/auto_explain/auto_explain.c
+++ b/contrib/auto_explain/auto_explain.c
@@ -15,6 +15,7 @@
 #include <limits.h>
 
 #include "access/parallel.h"
+#include "commands/explain.h"
 #include "commands/explain_format.h"
 #include "commands/explain_state.h"
 #include "common/pg_prng.h"
diff --git a/contrib/file_fdw/file_fdw.c b/contrib/file_fdw/file_fdw.c
index 05b01a305c1..56ececac70b 100644
--- a/contrib/file_fdw/file_fdw.c
+++ b/contrib/file_fdw/file_fdw.c
@@ -27,6 +27,7 @@
 #include "commands/explain_format.h"
 #include "commands/explain_state.h"
 #include "commands/vacuum.h"
+#include "executor/executor.h"
 #include "foreign/fdwapi.h"
 #include "foreign/foreign.h"
 #include "miscadmin.h"
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index 7c203420fed..a3d40c9934f 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -51,6 +51,9 @@ ExplainOneQuery_hook_type ExplainOneQuery_hook = NULL;
 /* Hook for plugins to get control in explain_get_index_name() */
 explain_get_index_name_hook_type explain_get_index_name_hook = NULL;
 
+/* per-plan and per-node hooks for plugins to print additional info */
+explain_per_plan_hook_type explain_per_plan_hook = NULL;
+explain_per_node_hook_type explain_per_node_hook = NULL;
 
 /*
  * Various places within need to convert bytes to kilobytes.  Round these up
@@ -647,6 +650,11 @@ ExplainOnePlan(PlannedStmt *plannedstmt, CachedPlan *cplan,
 	if (es->serialize != EXPLAIN_SERIALIZE_NONE)
 		ExplainPrintSerialize(es, &serializeMetrics);
 
+	/* Allow plugins to print additional information */
+	if (explain_per_plan_hook)
+		(*explain_per_plan_hook) (plannedstmt, into, es, queryString,
+								  params, queryEnv);
+
 	/*
 	 * Close down the query and free resources.  Include time for this in the
 	 * total execution time (although it should be pretty minimal).
@@ -2307,6 +2315,11 @@ ExplainNode(PlanState *planstate, List *ancestors,
 		ExplainFlushWorkersState(es);
 	es->workers_state = save_workers_state;
 
+	/* Allow plugins to print additional information */
+	if (explain_per_node_hook)
+		(*explain_per_node_hook) (planstate, ancestors, relationship,
+								  plan_name, es);
+
 	/*
 	 * If partition pruning was done during executor initialization, the
 	 * number of child plans we'll display below will be less than the number
diff --git a/src/include/commands/explain.h b/src/include/commands/explain.h
index 7a091c3c505..32883aa6398 100644
--- a/src/include/commands/explain.h
+++ b/src/include/commands/explain.h
@@ -30,6 +30,23 @@ typedef void (*ExplainOneQuery_hook_type) (Query *query,
 										   QueryEnvironment *queryEnv);
 extern PGDLLIMPORT ExplainOneQuery_hook_type ExplainOneQuery_hook;
 
+/* Hook for EXPLAIN plugins to print extra information for each plan */
+typedef void (*explain_per_plan_hook_type) (PlannedStmt *plannedstmt,
+											IntoClause *into,
+											ExplainState *es,
+											const char *queryString,
+											ParamListInfo params,
+											QueryEnvironment *queryEnv);
+extern PGDLLIMPORT explain_per_plan_hook_type explain_per_plan_hook;
+
+/* Hook for EXPLAIN plugins to print extra fields on individual plan nodes */
+typedef void (*explain_per_node_hook_type) (PlanState *planstate,
+											List *ancestors,
+											const char *relationship,
+											const char *plan_name,
+											ExplainState *es);
+extern PGDLLIMPORT explain_per_node_hook_type explain_per_node_hook;
+
 /* Hook for plugins to get control in explain_get_index_name() */
 typedef const char *(*explain_get_index_name_hook_type) (Oid indexId);
 extern PGDLLIMPORT explain_get_index_name_hook_type explain_get_index_name_hook;
diff --git a/src/include/commands/explain_state.h b/src/include/commands/explain_state.h
index a753bd44c5e..694f4699605 100644
--- a/src/include/commands/explain_state.h
+++ b/src/include/commands/explain_state.h
@@ -13,6 +13,10 @@
 #ifndef EXPLAIN_STATE_H
 #define EXPLAIN_STATE_H
 
+#include "nodes/parsenodes.h"
+#include "nodes/plannodes.h"
+#include "parser/parse_node.h"
+
 struct ExplainState;
 typedef struct ExplainState ExplainState;
 
-- 
2.39.3 (Apple Git-145)

