Improve support for ExplainOneQuery() hook
authorMichael Paquier <michael@paquier.xyz>
Sun, 10 Mar 2024 23:40:40 +0000 (08:40 +0900)
committerMichael Paquier <michael@paquier.xyz>
Sun, 10 Mar 2024 23:40:40 +0000 (08:40 +0900)
There is a hook called ExplainOneQuery_hook that gives modules the
possibility to plug into this code path, but, like utility.c for utility
statement execution, there is no corresponding "standard" routine in
the case of EXPLAIN executed for one Query.

This commit adds a new standard_ExplainOneQuery() in explain.c, which is
able to run explain on a non-utility Query without calling its hook.

Per the feedback received from a couple of hackers, this change gives
the possibility to cut a few hundred lines of code in some of the
popular out-of-core modules as these maintained a copy of
ExplainOneQuery(), adding custom extra information at the beginning or
the end of the EXPLAIN output.

Author: Mats Kindahl
Reviewed-by: Aleksander Alekseev, Jelte Fennema-Nio, Andrei Lepikhov
Discussion: https://postgr.es/m/CA+14427V_B4EAoC_o-iYYucRdMSOTfpuH9k-QbexffY1HYJBiA@mail.gmail.com

src/backend/commands/explain.c
src/include/commands/explain.h

index 78754bc6ba01783c7b66183e6698e00ab1efd4c6..a9d5056af48cba49f1fbb806a1e511bf48af07aa 100644 (file)
@@ -394,60 +394,72 @@ ExplainOneQuery(Query *query, int cursorOptions,
        (*ExplainOneQuery_hook) (query, cursorOptions, into, es,
                                 queryString, params, queryEnv);
    else
-   {
-       PlannedStmt *plan;
-       instr_time  planstart,
-                   planduration;
-       BufferUsage bufusage_start,
-                   bufusage;
-       MemoryContextCounters mem_counters;
-       MemoryContext planner_ctx = NULL;
-       MemoryContext saved_ctx = NULL;
-
-       if (es->memory)
-       {
-           /*
-            * Create a new memory context to measure planner's memory
-            * consumption accurately.  Note that if the planner were to be
-            * modified to use a different memory context type, here we would
-            * be changing that to AllocSet, which might be undesirable.
-            * However, we don't have a way to create a context of the same
-            * type as another, so we pray and hope that this is OK.
-            */
-           planner_ctx = AllocSetContextCreate(CurrentMemoryContext,
-                                               "explain analyze planner context",
-                                               ALLOCSET_DEFAULT_SIZES);
-           saved_ctx = MemoryContextSwitchTo(planner_ctx);
-       }
+       standard_ExplainOneQuery(query, cursorOptions, into, es,
+                                queryString, params, queryEnv);
+}
 
-       if (es->buffers)
-           bufusage_start = pgBufferUsage;
-       INSTR_TIME_SET_CURRENT(planstart);
+/*
+ * standard_ExplainOneQuery -
+ *   print out the execution plan for one Query, without calling a hook.
+ */
+void
+standard_ExplainOneQuery(Query *query, int cursorOptions,
+                        IntoClause *into, ExplainState *es,
+                        const char *queryString, ParamListInfo params,
+                        QueryEnvironment *queryEnv)
+{
+   PlannedStmt *plan;
+   instr_time  planstart,
+               planduration;
+   BufferUsage bufusage_start,
+               bufusage;
+   MemoryContextCounters mem_counters;
+   MemoryContext planner_ctx = NULL;
+   MemoryContext saved_ctx = NULL;
+
+   if (es->memory)
+   {
+       /*
+        * Create a new memory context to measure planner's memory consumption
+        * accurately.  Note that if the planner were to be modified to use a
+        * different memory context type, here we would be changing that to
+        * AllocSet, which might be undesirable.  However, we don't have a way
+        * to create a context of the same type as another, so we pray and
+        * hope that this is OK.
+        */
+       planner_ctx = AllocSetContextCreate(CurrentMemoryContext,
+                                           "explain analyze planner context",
+                                           ALLOCSET_DEFAULT_SIZES);
+       saved_ctx = MemoryContextSwitchTo(planner_ctx);
+   }
 
-       /* plan the query */
-       plan = pg_plan_query(query, queryString, cursorOptions, params);
+   if (es->buffers)
+       bufusage_start = pgBufferUsage;
+   INSTR_TIME_SET_CURRENT(planstart);
 
-       INSTR_TIME_SET_CURRENT(planduration);
-       INSTR_TIME_SUBTRACT(planduration, planstart);
+   /* plan the query */
+   plan = pg_plan_query(query, queryString, cursorOptions, params);
 
-       if (es->memory)
-       {
-           MemoryContextSwitchTo(saved_ctx);
-           MemoryContextMemConsumed(planner_ctx, &mem_counters);
-       }
+   INSTR_TIME_SET_CURRENT(planduration);
+   INSTR_TIME_SUBTRACT(planduration, planstart);
 
-       /* calc differences of buffer counters. */
-       if (es->buffers)
-       {
-           memset(&bufusage, 0, sizeof(BufferUsage));
-           BufferUsageAccumDiff(&bufusage, &pgBufferUsage, &bufusage_start);
-       }
+   if (es->memory)
+   {
+       MemoryContextSwitchTo(saved_ctx);
+       MemoryContextMemConsumed(planner_ctx, &mem_counters);
+   }
 
-       /* run it (if needed) and produce output */
-       ExplainOnePlan(plan, into, es, queryString, params, queryEnv,
-                      &planduration, (es->buffers ? &bufusage : NULL),
-                      es->memory ? &mem_counters : NULL);
+   /* calc differences of buffer counters. */
+   if (es->buffers)
+   {
+       memset(&bufusage, 0, sizeof(BufferUsage));
+       BufferUsageAccumDiff(&bufusage, &pgBufferUsage, &bufusage_start);
    }
+
+   /* run it (if needed) and produce output */
+   ExplainOnePlan(plan, into, es, queryString, params, queryEnv,
+                  &planduration, (es->buffers ? &bufusage : NULL),
+                  es->memory ? &mem_counters : NULL);
 }
 
 /*
index 7c0f0b56365289a25ad1c121f82592ff0010bb57..cf195f1359761795bac733f5793e230582ad82b8 100644 (file)
@@ -80,6 +80,10 @@ extern PGDLLIMPORT explain_get_index_name_hook_type explain_get_index_name_hook;
 
 extern void ExplainQuery(ParseState *pstate, ExplainStmt *stmt,
                         ParamListInfo params, DestReceiver *dest);
+extern void standard_ExplainOneQuery(Query *query, int cursorOptions,
+                                    IntoClause *into, ExplainState *es,
+                                    const char *queryString, ParamListInfo params,
+                                    QueryEnvironment *queryEnv);
 
 extern ExplainState *NewExplainState(void);