]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Add ExplainState argument to pg_plan_query() and planner().
authorRobert Haas <rhaas@postgresql.org>
Wed, 8 Oct 2025 12:33:29 +0000 (08:33 -0400)
committerRobert Haas <rhaas@postgresql.org>
Wed, 8 Oct 2025 12:33:29 +0000 (08:33 -0400)
This allows extensions to have access to any data they've stored
in the ExplainState during planning. Unfortunately, it won't help
with EXPLAIN EXECUTE is used, but since that case is less common,
this still seems like an improvement.

Since planner() has quite a few arguments now, also add some
documentation of those arguments and the return value.

Author: Robert Haas <rhaas@postgresql.org>
Co-authored-by: Tom Lane <tgl@sss.pgh.pa.us>
Reviewed-by: Andrei Lepikhov <lepihov@gmail.com>
Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>
Discussion: http://postgr.es/m/CA+TgmoYWKHU2hKr62Toyzh-kTDEnMDeLw7gkOOnjL-TnOUq0kQ@mail.gmail.com

12 files changed:
contrib/pg_stat_statements/pg_stat_statements.c
src/backend/commands/copyto.c
src/backend/commands/createas.c
src/backend/commands/explain.c
src/backend/commands/matview.c
src/backend/commands/portalcmds.c
src/backend/optimizer/plan/planner.c
src/backend/tcop/postgres.c
src/include/optimizer/optimizer.h
src/include/optimizer/planner.h
src/include/tcop/tcopprot.h
src/test/modules/delay_execution/delay_execution.c

index db1af36a705820a29327fa89ae0931ffd2341bf1..f2187167c5cb48237e58d073229b73717e5886fe 100644 (file)
@@ -338,7 +338,8 @@ static void pgss_post_parse_analyze(ParseState *pstate, Query *query,
 static PlannedStmt *pgss_planner(Query *parse,
                                                                 const char *query_string,
                                                                 int cursorOptions,
-                                                                ParamListInfo boundParams);
+                                                                ParamListInfo boundParams,
+                                                                ExplainState *es);
 static void pgss_ExecutorStart(QueryDesc *queryDesc, int eflags);
 static void pgss_ExecutorRun(QueryDesc *queryDesc,
                                                         ScanDirection direction,
@@ -894,7 +895,8 @@ static PlannedStmt *
 pgss_planner(Query *parse,
                         const char *query_string,
                         int cursorOptions,
-                        ParamListInfo boundParams)
+                        ParamListInfo boundParams,
+                        ExplainState *es)
 {
        PlannedStmt *result;
 
@@ -929,10 +931,10 @@ pgss_planner(Query *parse,
                {
                        if (prev_planner_hook)
                                result = prev_planner_hook(parse, query_string, cursorOptions,
-                                                                                  boundParams);
+                                                                                  boundParams, es);
                        else
                                result = standard_planner(parse, query_string, cursorOptions,
-                                                                                 boundParams);
+                                                                                 boundParams, es);
                }
                PG_FINALLY();
                {
@@ -978,10 +980,10 @@ pgss_planner(Query *parse,
                {
                        if (prev_planner_hook)
                                result = prev_planner_hook(parse, query_string, cursorOptions,
-                                                                                  boundParams);
+                                                                                  boundParams, es);
                        else
                                result = standard_planner(parse, query_string, cursorOptions,
-                                                                                 boundParams);
+                                                                                 boundParams, es);
                }
                PG_FINALLY();
                {
index 67b94b91cae44427b7bb35bb839501f9a732df30..e5781155cdfa8756458945986e0beced03cb6add 100644 (file)
@@ -796,7 +796,7 @@ BeginCopyTo(ParseState *pstate,
 
                /* plan the query */
                plan = pg_plan_query(query, pstate->p_sourcetext,
-                                                        CURSOR_OPT_PARALLEL_OK, NULL);
+                                                        CURSOR_OPT_PARALLEL_OK, NULL, NULL);
 
                /*
                 * With row-level security and a user using "COPY relation TO", we
index dfd2ab8e8628c7a32a9a44ad3ec1adff3a7e4a27..1ccc2e55c6434583cc7eac3bfdcc1babfc7d38a3 100644 (file)
@@ -321,7 +321,7 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 
                /* plan the query */
                plan = pg_plan_query(query, pstate->p_sourcetext,
-                                                        CURSOR_OPT_PARALLEL_OK, params);
+                                                        CURSOR_OPT_PARALLEL_OK, params, NULL);
 
                /*
                 * Use a snapshot with an updated command ID to ensure this query sees
index 06191cd8a85da425d30372041f65692d954ced0e..e6edae0845cb06b880213d5cfc6869465dd85cd0 100644 (file)
@@ -351,7 +351,7 @@ standard_ExplainOneQuery(Query *query, int cursorOptions,
        INSTR_TIME_SET_CURRENT(planstart);
 
        /* plan the query */
-       plan = pg_plan_query(query, queryString, cursorOptions, params);
+       plan = pg_plan_query(query, queryString, cursorOptions, params, es);
 
        INSTR_TIME_SET_CURRENT(planduration);
        INSTR_TIME_SUBTRACT(planduration, planstart);
index 188e26f0e6e29d9f44f15e2423543b13cb51d203..441de55ac24f2723f515be8e1f4bdaa4bfd340e1 100644 (file)
@@ -426,7 +426,7 @@ refresh_matview_datafill(DestReceiver *dest, Query *query,
        CHECK_FOR_INTERRUPTS();
 
        /* Plan the query which will generate data for the refresh. */
-       plan = pg_plan_query(query, queryString, CURSOR_OPT_PARALLEL_OK, NULL);
+       plan = pg_plan_query(query, queryString, CURSOR_OPT_PARALLEL_OK, NULL, NULL);
 
        /*
         * Use a snapshot with an updated command ID to ensure this query sees
index e7c8171c1020717e7fdf89dc6a01861806fa5fe9..ec96c2efcd36abe6181053539fce3523359ec558 100644 (file)
@@ -99,7 +99,8 @@ PerformCursorOpen(ParseState *pstate, DeclareCursorStmt *cstmt, ParamListInfo pa
                elog(ERROR, "non-SELECT statement in DECLARE CURSOR");
 
        /* Plan the query, applying the specified options */
-       plan = pg_plan_query(query, pstate->p_sourcetext, cstmt->options, params);
+       plan = pg_plan_query(query, pstate->p_sourcetext, cstmt->options, params,
+                                                NULL);
 
        /*
         * Create a portal and copy the plan and query string into its memory.
index 6f06174cdab063cecd366c37968817077ac8af42..c250433a2ef43bbcd95579e4cf1627b9a4463514 100644 (file)
@@ -279,6 +279,23 @@ static void create_partial_unique_paths(PlannerInfo *root, RelOptInfo *input_rel
  *
  *        Query optimizer entry point
  *
+ * Inputs:
+ *     parse: an analyzed-and-rewritten query tree for an optimizable statement
+ *     query_string: source text for the query tree (used for error reports)
+ *     cursorOptions: bitmask of CURSOR_OPT_XXX flags, see parsenodes.h
+ *     boundParams: passed-in parameter values, or NULL if none
+ *     es: ExplainState if being called from EXPLAIN, else NULL
+ *
+ * The result is a PlannedStmt tree.
+ *
+ * PARAM_EXTERN Param nodes within the parse tree can be replaced by Consts
+ * using values from boundParams, if those values are marked PARAM_FLAG_CONST.
+ * Parameter values not so marked are still relied on for estimation purposes.
+ *
+ * The ExplainState pointer is not currently used by the core planner, but it
+ * is passed through to some planner hooks so that they can report information
+ * back to EXPLAIN extension hooks.
+ *
  * To support loadable plugins that monitor or modify planner behavior,
  * we provide a hook variable that lets a plugin get control before and
  * after the standard planning process.  The plugin would normally call
@@ -290,14 +307,16 @@ static void create_partial_unique_paths(PlannerInfo *root, RelOptInfo *input_rel
  *****************************************************************************/
 PlannedStmt *
 planner(Query *parse, const char *query_string, int cursorOptions,
-               ParamListInfo boundParams)
+               ParamListInfo boundParams, ExplainState *es)
 {
        PlannedStmt *result;
 
        if (planner_hook)
-               result = (*planner_hook) (parse, query_string, cursorOptions, boundParams);
+               result = (*planner_hook) (parse, query_string, cursorOptions,
+                                                                 boundParams, es);
        else
-               result = standard_planner(parse, query_string, cursorOptions, boundParams);
+               result = standard_planner(parse, query_string, cursorOptions,
+                                                                 boundParams, es);
 
        pgstat_report_plan_id(result->planId, false);
 
@@ -306,7 +325,7 @@ planner(Query *parse, const char *query_string, int cursorOptions,
 
 PlannedStmt *
 standard_planner(Query *parse, const char *query_string, int cursorOptions,
-                                ParamListInfo boundParams)
+                                ParamListInfo boundParams, ExplainState *es)
 {
        PlannedStmt *result;
        PlannerGlobal *glob;
index d356830f756beb55ecf9aa9836214682db918245..7dd75a490aab54e6066864bc4f0edec9e02563ae 100644 (file)
@@ -37,6 +37,7 @@
 #include "catalog/pg_type.h"
 #include "commands/async.h"
 #include "commands/event_trigger.h"
+#include "commands/explain_state.h"
 #include "commands/prepare.h"
 #include "common/pg_prng.h"
 #include "jit/jit.h"
@@ -884,7 +885,7 @@ pg_rewrite_query(Query *query)
  */
 PlannedStmt *
 pg_plan_query(Query *querytree, const char *query_string, int cursorOptions,
-                         ParamListInfo boundParams)
+                         ParamListInfo boundParams, ExplainState *es)
 {
        PlannedStmt *plan;
 
@@ -901,7 +902,7 @@ pg_plan_query(Query *querytree, const char *query_string, int cursorOptions,
                ResetUsage();
 
        /* call the optimizer */
-       plan = planner(querytree, query_string, cursorOptions, boundParams);
+       plan = planner(querytree, query_string, cursorOptions, boundParams, es);
 
        if (log_planner_stats)
                ShowUsage("PLANNER STATISTICS");
@@ -997,7 +998,7 @@ pg_plan_queries(List *querytrees, const char *query_string, int cursorOptions,
                else
                {
                        stmt = pg_plan_query(query, query_string, cursorOptions,
-                                                                boundParams);
+                                                                boundParams, NULL);
                }
 
                stmt_list = lappend(stmt_list, stmt);
index 04878f1f1c2241309ffa3673eb05adddf49d52d5..a34113903c0e0f0a60a47e81fe8045727d7eff00 100644 (file)
@@ -24,6 +24,8 @@
 
 #include "nodes/parsenodes.h"
 
+typedef struct ExplainState ExplainState;      /* defined in explain_state.h */
+
 /*
  * We don't want to include nodes/pathnodes.h here, because non-planner
  * code should generally treat PlannerInfo as an opaque typedef.
@@ -104,7 +106,8 @@ extern PGDLLIMPORT bool enable_distinct_reordering;
 
 extern PlannedStmt *planner(Query *parse, const char *query_string,
                                                        int cursorOptions,
-                                                       ParamListInfo boundParams);
+                                                       ParamListInfo boundParams,
+                                                       ExplainState *es);
 
 extern Expr *expression_planner(Expr *expr);
 extern Expr *expression_planner_with_deps(Expr *expr,
index 1bbef0018d54461b836d3f35aa3d8ff2005ac76d..c7ab466f5f1f4bf6564e78a033709bfdf279baca 100644 (file)
 #include "nodes/plannodes.h"
 
 
+typedef struct ExplainState ExplainState;      /* defined in explain_state.h */
+
 /* Hook for plugins to get control in planner() */
 typedef PlannedStmt *(*planner_hook_type) (Query *parse,
                                                                                   const char *query_string,
                                                                                   int cursorOptions,
-                                                                                  ParamListInfo boundParams);
+                                                                                  ParamListInfo boundParams,
+                                                                                  ExplainState *es);
 extern PGDLLIMPORT planner_hook_type planner_hook;
 
 /* Hook for plugins to get control when grouping_planner() plans upper rels */
@@ -40,7 +43,8 @@ extern PGDLLIMPORT create_upper_paths_hook_type create_upper_paths_hook;
 
 extern PlannedStmt *standard_planner(Query *parse, const char *query_string,
                                                                         int cursorOptions,
-                                                                        ParamListInfo boundParams);
+                                                                        ParamListInfo boundParams,
+                                                                        ExplainState *es);
 
 extern PlannerInfo *subquery_planner(PlannerGlobal *glob, Query *parse,
                                                                         char *plan_name,
index a83cc4f48501601d7b51b4178effc4aa11cf111f..c1bcfdec673cbd232598a3314feaeacdd2a5c8d1 100644 (file)
@@ -20,6 +20,7 @@
 #include "utils/guc.h"
 #include "utils/queryenvironment.h"
 
+typedef struct ExplainState ExplainState;      /* defined in explain_state.h */
 
 extern PGDLLIMPORT CommandDest whereToSendOutput;
 extern PGDLLIMPORT const char *debug_query_string;
@@ -63,7 +64,8 @@ extern List *pg_analyze_and_rewrite_withcb(RawStmt *parsetree,
                                                                                   QueryEnvironment *queryEnv);
 extern PlannedStmt *pg_plan_query(Query *querytree, const char *query_string,
                                                                  int cursorOptions,
-                                                                 ParamListInfo boundParams);
+                                                                 ParamListInfo boundParams,
+                                                                 ExplainState *es);
 extern List *pg_plan_queries(List *querytrees, const char *query_string,
                                                         int cursorOptions,
                                                         ParamListInfo boundParams);
index 7bc97f84a1c3cf4dfd96818f3b8cff023d3924ec..d933e9a6e539765169831f2d1bf59aa751a4e1e0 100644 (file)
@@ -40,17 +40,18 @@ static planner_hook_type prev_planner_hook = NULL;
 /* planner_hook function to provide the desired delay */
 static PlannedStmt *
 delay_execution_planner(Query *parse, const char *query_string,
-                                               int cursorOptions, ParamListInfo boundParams)
+                                               int cursorOptions, ParamListInfo boundParams,
+                                               ExplainState *es)
 {
        PlannedStmt *result;
 
        /* Invoke the planner, possibly via a previous hook user */
        if (prev_planner_hook)
                result = prev_planner_hook(parse, query_string, cursorOptions,
-                                                                  boundParams);
+                                                                  boundParams, es);
        else
                result = standard_planner(parse, query_string, cursorOptions,
-                                                                 boundParams);
+                                                                 boundParams, es);
 
        /* If enabled, delay by taking and releasing the specified lock */
        if (post_planning_lock_id != 0)