]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Add an alternative_plan_name field to PlannerInfo.
authorRobert Haas <rhaas@postgresql.org>
Thu, 26 Mar 2026 20:45:17 +0000 (16:45 -0400)
committerRobert Haas <rhaas@postgresql.org>
Thu, 26 Mar 2026 20:45:17 +0000 (16:45 -0400)
Typically, we have only one PlannerInfo for any given subquery, but
when we are considering a MinMaxAggPath or a hashed subplan, we end
up creating a second PlannerInfo for the same portion of the query,
with a clone of the original range table. In fact, in the MinMaxAggPath
case, we might end up creating several clones, one per aggregate.

At present, there's no easy way for a plugin, such as pg_plan_advice,
to understand the relationships between the original range table and
the copies of it that are created in these cases.  To fix, add an
alternative_plan_name field to PlannerInfo. For a hashed subplan, this
is the plan name for the non-hashed alternative; for minmax aggregates,
this is the plan_name from the parent PlannerInfo; otherwise, it's the
same as plan_name.

Discussion: http://postgr.es/m/CA+TgmoYuWmN-00Ec5pY7zAcpSFQUQLbgAdVWGR9kOR-HM-fHrA@mail.gmail.com
Reviewed-by: Lukas Fittl <lukas@fittl.com>
src/backend/optimizer/path/allpaths.c
src/backend/optimizer/plan/planagg.c
src/backend/optimizer/plan/planner.c
src/backend/optimizer/plan/subselect.c
src/backend/optimizer/prep/prepjointree.c
src/backend/optimizer/prep/prepunion.c
src/include/nodes/pathnodes.h
src/include/optimizer/planner.h

index c26f48edfa01070d1a6b82af8d97943a71cc0f2d..61093f222a124e598bdfe7f93b29423d4affb359 100644 (file)
@@ -2833,7 +2833,7 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
        /* Generate a subroot and Paths for the subquery */
        plan_name = choose_plan_name(root->glob, rte->eref->aliasname, false);
        rel->subroot = subquery_planner(root->glob, subquery, plan_name,
-                                                                       root, false, tuple_fraction, NULL);
+                                                                       root, NULL, false, tuple_fraction, NULL);
 
        /* Isolate the params needed by this specific subplan */
        rel->subplan_params = root->plan_params;
index 09b38b2c378bd7e9cf985a596385e0629a6b9eec..75f6475cb562e1e22adb4005e4f855eb93d370ba 100644 (file)
@@ -341,6 +341,7 @@ build_minmax_path(PlannerInfo *root, MinMaxAggInfo *mminfo,
        subroot->query_level++;
        subroot->parent_root = root;
        subroot->plan_name = choose_plan_name(root->glob, "minmax", true);
+       subroot->alternative_plan_name = root->plan_name;
 
        /* reset subplan-related stuff */
        subroot->plan_params = NIL;
index 42604a0f75c1973e993bf5ea3fef334c565d6a3a..d19800ad6a5218217b387cfeefa22a61b10d58bc 100644 (file)
@@ -515,8 +515,8 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions,
                                                           &tuple_fraction, es);
 
        /* primary planning entry point (may recurse for subqueries) */
-       root = subquery_planner(glob, parse, NULL, NULL, false, tuple_fraction,
-                                                       NULL);
+       root = subquery_planner(glob, parse, NULL, NULL, NULL, false,
+                                                       tuple_fraction, NULL);
 
        /* Select best Path and turn it into a Plan */
        final_rel = fetch_upper_rel(root, UPPERREL_FINAL, NULL);
@@ -715,6 +715,8 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions,
  * parse is the querytree produced by the parser & rewriter.
  * plan_name is the name to assign to this subplan (NULL at the top level).
  * parent_root is the immediate parent Query's info (NULL at the top level).
+ * alternative_root is a previously created PlannerInfo for which this query
+ * level is an alternative implementation, or else NULL.
  * hasRecursion is true if this is a recursive WITH query.
  * tuple_fraction is the fraction of tuples we expect will be retrieved.
  * tuple_fraction is interpreted as explained for grouping_planner, below.
@@ -741,8 +743,9 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions,
  */
 PlannerInfo *
 subquery_planner(PlannerGlobal *glob, Query *parse, char *plan_name,
-                                PlannerInfo *parent_root, bool hasRecursion,
-                                double tuple_fraction, SetOperationStmt *setops)
+                                PlannerInfo *parent_root, PlannerInfo *alternative_root,
+                                bool hasRecursion, double tuple_fraction,
+                                SetOperationStmt *setops)
 {
        PlannerInfo *root;
        List       *newWithCheckOptions;
@@ -758,6 +761,10 @@ subquery_planner(PlannerGlobal *glob, Query *parse, char *plan_name,
        root->glob = glob;
        root->query_level = parent_root ? parent_root->query_level + 1 : 1;
        root->plan_name = plan_name;
+       if (alternative_root != NULL)
+               root->alternative_plan_name = alternative_root->plan_name;
+       else
+               root->alternative_plan_name = plan_name;
        root->parent_root = parent_root;
        root->plan_params = NIL;
        root->outer_params = NULL;
index 0d31861da7f4936c22bef4f110cab005daa6c0d0..ccec1eaa7fe16ad574307155fcb07ab635192a96 100644 (file)
@@ -224,7 +224,7 @@ make_subplan(PlannerInfo *root, Query *orig_subquery,
        /* Generate Paths for the subquery */
        subroot = subquery_planner(root->glob, subquery,
                                                           choose_plan_name(root->glob, sublinkstr, true),
-                                                          root, false, tuple_fraction, NULL);
+                                                          root, NULL, false, tuple_fraction, NULL);
 
        /* Isolate the params needed by this specific subplan */
        plan_params = root->plan_params;
@@ -274,7 +274,7 @@ make_subplan(PlannerInfo *root, Query *orig_subquery,
                        /* Generate Paths for the ANY subquery; we'll need all rows */
                        plan_name = choose_plan_name(root->glob, sublinkstr, true);
                        subroot = subquery_planner(root->glob, subquery, plan_name,
-                                                                          root, false, 0.0, NULL);
+                                                                          root, subroot, false, 0.0, NULL);
 
                        /* Isolate the params needed by this specific subplan */
                        plan_params = root->plan_params;
@@ -971,7 +971,7 @@ SS_process_ctes(PlannerInfo *root)
                 */
                subroot = subquery_planner(root->glob, subquery,
                                                                   choose_plan_name(root->glob, cte->ctename, false),
-                                                                  root, cte->cterecursive, 0.0, NULL);
+                                                                  root, NULL, cte->cterecursive, 0.0, NULL);
 
                /*
                 * Since the current query level doesn't yet contain any RTEs, it
index d5e1041ffa332819c8ff84191cb5759cc4021778..95bf51606cc6466455b4b298221e457b4ba42b9d 100644 (file)
@@ -1418,6 +1418,7 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
        subroot->glob = root->glob;
        subroot->query_level = root->query_level;
        subroot->plan_name = root->plan_name;
+       subroot->alternative_plan_name = root->alternative_plan_name;
        subroot->parent_root = root->parent_root;
        subroot->plan_params = NIL;
        subroot->outer_params = NULL;
index 583cb0b7a2553c95e6cebda55fa09372f6738074..d1f022c5bfddc4bbb431d5ae293cf863c719b6b8 100644 (file)
@@ -250,7 +250,7 @@ recurse_set_operations(Node *setOp, PlannerInfo *root,
                 */
                plan_name = choose_plan_name(root->glob, "setop", true);
                subroot = rel->subroot = subquery_planner(root->glob, subquery,
-                                                                                                 plan_name, root,
+                                                                                                 plan_name, root, NULL,
                                                                                                  false, root->tuple_fraction,
                                                                                                  parentOp);
 
index 27758ec16fe66425e7f566141c6a96d10d462180..96ec32272624da1e738a71ae7f380fcb2bec25bf 100644 (file)
@@ -320,6 +320,18 @@ struct PlannerInfo
        /* Subplan name for EXPLAIN and debugging purposes (NULL at top level) */
        char       *plan_name;
 
+       /*
+        * If this PlannerInfo exists to consider an alternative implementation
+        * strategy for a portion of the query that could also be implemented by
+        * some other PlannerInfo, this is the plan_name for that other
+        * PlannerInfo. When we are considering the first or only alternative,
+        * it is the same as plan_name.
+        *
+        * Currently, we set this to a value other than plan_name only when
+        * considering a MinMaxAggPath or a hashed SubPlan.
+        */
+       char       *alternative_plan_name;
+
        /*
         * plan_params contains the expressions that this query level needs to
         * make available to a lower query level that is currently being planned.
index 80509773c01bd2a057a16143a234c7bde65b0766..9c4950b340ffb71b60aa2b4dd959022d2aef1684 100644 (file)
@@ -63,6 +63,7 @@ extern PlannedStmt *standard_planner(Query *parse, const char *query_string,
 extern PlannerInfo *subquery_planner(PlannerGlobal *glob, Query *parse,
                                                                         char *plan_name,
                                                                         PlannerInfo *parent_root,
+                                                                        PlannerInfo *alternative_root,
                                                                         bool hasRecursion, double tuple_fraction,
                                                                         SetOperationStmt *setops);