hackity, hack
authorRobert Haas <rhaas@postgresql.org>
Thu, 29 May 2025 17:49:03 +0000 (13:49 -0400)
committerRobert Haas <rhaas@postgresql.org>
Thu, 29 May 2025 17:49:03 +0000 (13:49 -0400)
contrib/pg_plan_advice/pgpa_join.c
contrib/pg_plan_advice/pgpa_join.h
contrib/pg_plan_advice/pgpa_walker.c
contrib/pg_plan_advice/pgpa_walker.h

index e51d6ed632c2a3a44f960349a24b39308476d6cf..ce32f80d53773fdc60ccff97cda9fc92c5729d44 100644 (file)
@@ -1,6 +1,7 @@
 #include "postgres.h"
 
 #include "pgpa_join.h"
+#include "pgpa_walker.h"
 
 /*
  * Temporary object used when unrolling a join tree.
@@ -10,14 +11,20 @@ struct pgpa_join_unroller
        unsigned        nallocated;
        unsigned        nused;
        Plan       *outer_subplan;
+       ElidedNode *outer_elided_node;
        pgpa_join_strategy *strategy;
        Plan      **inner_subplans;
-       pgpa_join_unroller **inner_unroller;
+       ElidedNode **inner_elided_nodes;
+       pgpa_join_unroller **inner_unrollers;
 };
 
-static pgpa_join_strategy pgpa_decompose_join(Plan *plan,
+static pgpa_join_strategy pgpa_decompose_join(PlannedStmt *pstmt,
+                                                                                         Plan *plan,
+                                                                                         Plan **realouter,
                                                                                          Plan **realinner,
-                                                                                         Plan **realouter);
+                                                                                         ElidedNode **elidedrealouter,
+                                                                                         ElidedNode **elidedrealinner);
+static void pgpa_fix_scan_or_clump_member(pgpa_join_member *member);
 static Index pgpa_scanrelid(Plan *plan);
 static Bitmapset *pgpa_relids(Plan *plan);
 
@@ -51,27 +58,52 @@ pgpa_get_join_class(Plan *plan)
 
 /*
  * Build a pgpa_clumped_join object for a Plan node.
+ *
+ * If there is at least one ElidedNode for this plan node, pass the uppermost
+ * one as elided_node, else pass NULL.
  */
 pgpa_clumped_join *
-pgpa_build_clumped_join(Plan *plan)
+pgpa_build_clumped_join(Plan *plan, ElidedNode *elided_node)
 {
        pgpa_clumped_join *clump = palloc(sizeof(pgpa_clumped_join));
 
        Assert(pgpa_get_join_class(plan) == PGPA_CLUMPED_JOIN);
 
        clump->plan = plan;
-       clump->relids = pgpa_relids(plan);
 
-       if (IsA(plan, Result))
-               clump->strategy = JSTRAT_CLUMP_DEGENERATE;
-       else if (IsA(plan, ForeignScan))
-               clump->strategy = JSTRAT_CLUMP_FOREIGN;
-       else if (IsA(plan, Append))
-               clump->strategy = JSTRAT_CLUMP_PARTITIONWISE;
-       else if (IsA(plan, MergeAppend))
-               clump->strategy = JSTRAT_CLUMP_PARTITIONWISE;
+       if (elided_node != NULL)
+       {
+               NodeTag         elided_type = elided_node->elided_type;
+
+               /*
+                * The only case we expect to encounter here is a partitionwise join
+                * whose Append or MergeAppend node was elided due to having only one
+                * surviving child. It's also possiblee for a trivial SubqueryScan to
+                * be elided, but in that case we expected only a single RTI, in which
+                * case it's not a join.
+                */
+               Assert(bms_membership(clump->relids) == BMS_MULTIPLE);
+               clump->relids = elided_node->relids;
+               if (elided_type == T_Append || elided_type == T_MergeAppend)
+                       clump->strategy = JSTRAT_CLUMP_PARTITIONWISE;
+               else
+                       elog(ERROR, "unexpected elided node type");
+       }
        else
-               elog(ERROR, "unexpected plan type");
+       {
+               clump->relids = pgpa_relids(plan);
+
+               if (IsA(plan, Result))
+                       clump->strategy = JSTRAT_CLUMP_DEGENERATE;
+               else if (IsA(plan, ForeignScan))
+                       clump->strategy = JSTRAT_CLUMP_FOREIGN;
+               else if (IsA(plan, Append))
+                       clump->strategy = JSTRAT_CLUMP_PARTITIONWISE;
+               else if (IsA(plan, MergeAppend))
+                       clump->strategy = JSTRAT_CLUMP_PARTITIONWISE;
+               else
+                       elog(ERROR, "unexpected plan type");
+       }
 
        return clump;
 }
@@ -95,7 +127,9 @@ pgpa_create_join_unroller(void)
                palloc_array(pgpa_join_strategy, join_unroller->nallocated);
        join_unroller->inner_subplans =
                palloc_array(Plan *, join_unroller->nallocated);
-       join_unroller->inner_unroller =
+       join_unroller->inner_elided_nodes =
+               palloc_array(ElidedNode *, join_unroller->nallocated);
+       join_unroller->inner_unrollers =
                palloc_array(pgpa_join_unroller *, join_unroller->nallocated);
 
        return join_unroller;
@@ -113,6 +147,8 @@ pgpa_unroll_join(PlannedStmt *pstmt, Plan *plan,
        pgpa_join_strategy strategy;
        Plan       *realinner,
                           *realouter;
+       ElidedNode *elidedinner,
+                          *elidedouter;
        int                     n;
 
        Assert(join_unroller != NULL);
@@ -134,7 +170,8 @@ pgpa_unroll_join(PlannedStmt *pstmt, Plan *plan,
         * this should be an unrollable join.
         */
        Assert(pgpa_get_join_class(plan) == PGPA_UNROLLED_JOIN);
-       strategy = pgpa_decompose_join(plan, &realinner, &realouter);
+       strategy = pgpa_decompose_join(pstmt, plan, &realouter, &realinner,
+                                                                  &elidedouter, &elidedinner);
 
        /* If our workspace is full, expand it. */
        if (join_unroller->nused >= join_unroller->nallocated)
@@ -148,8 +185,12 @@ pgpa_unroll_join(PlannedStmt *pstmt, Plan *plan,
                        repalloc_array(join_unroller->inner_subplans,
                                                   Plan *,
                                                   join_unroller->nallocated);
-               join_unroller->inner_unroller =
-                       repalloc_array(join_unroller->inner_unroller,
+               join_unroller->inner_elided_nodes =
+                       repalloc_array(join_unroller->inner_elided_nodes,
+                                                  ElidedNode *,
+                                                  join_unroller->nallocated);
+               join_unroller->inner_unrollers =
+                       repalloc_array(join_unroller->inner_unrollers,
                                                   pgpa_join_unroller *,
                                                   join_unroller->nallocated);
        }
@@ -161,10 +202,14 @@ pgpa_unroll_join(PlannedStmt *pstmt, Plan *plan,
         * into this object and must remember the outer side as the final outer
         * subplan.
         */
-       if (pgpa_get_join_class(realouter) == PGPA_UNROLLED_JOIN)
+       if (elidedouter == NULL &&
+               pgpa_get_join_class(realouter) == PGPA_UNROLLED_JOIN)
                *outer_join_unroller = join_unroller;
        else
+       {
                join_unroller->outer_subplan = realouter;
+               join_unroller->outer_elided_node = elidedouter;
+       }
 
        /*
         * Store the inner subplan. If it's an unrollable join, it needs to be
@@ -173,11 +218,13 @@ pgpa_unroll_join(PlannedStmt *pstmt, Plan *plan,
        n = join_unroller->nused++;
        join_unroller->strategy[n] = strategy;
        join_unroller->inner_subplans[n] = realinner;
-       if (pgpa_get_join_class(realinner) == PGPA_UNROLLED_JOIN)
+       join_unroller->inner_elided_nodes[n] = elidedinner;
+       if (elidedinner == NULL &&
+               pgpa_get_join_class(realinner) == PGPA_UNROLLED_JOIN)
                *inner_join_unroller = pgpa_create_join_unroller();
        else
                *inner_join_unroller = NULL;
-       join_unroller->inner_unroller[n] = *inner_join_unroller;
+       join_unroller->inner_unrollers[n] = *inner_join_unroller;
 }
 
 /*
@@ -205,9 +252,8 @@ pgpa_build_unrolled_join(PlannedStmt *pstmt,
 
        /* Handle the outermost join. */
        ujoin->outer.plan = join_unroller->outer_subplan;
-       ujoin->outer.rti = pgpa_scanrelid(ujoin->outer.plan);
-       if (pgpa_get_join_class(ujoin->outer.plan) == PGPA_CLUMPED_JOIN)
-               ujoin->outer.clump_join = pgpa_build_clumped_join(ujoin->outer.plan);
+       ujoin->outer.elided_node = join_unroller->outer_elided_node;
+       pgpa_fix_scan_or_clump_member(&ujoin->outer);
 
        /*
         * We want the joins from the deepest part of the plan tree to appear
@@ -217,31 +263,23 @@ pgpa_build_unrolled_join(PlannedStmt *pstmt,
         */
        for (i = 0; i < join_unroller->nused; ++i)
        {
-               int             k = join_unroller->nused - i - 1;
+               int                     k = join_unroller->nused - i - 1;
 
-               /* Copy strategy and Plan pointer. */
+               /* Copy strategy, Plan, and ElidedNode. */
                ujoin->strategy[i] = join_unroller->strategy[k];
                ujoin->inner[i].plan = join_unroller->inner_subplans[k];
+               ujoin->inner[i].elided_node = join_unroller->inner_elided_nodes[k];
 
                /*
-                * If there is a nested pgpa_join_unroller, we need to convert it
-                * to a pgpa_unrolled_join and store the result.
+                * Fill in remaining details, using either the nested join unroller,
+                * or by deriving them from the plan and elided nodes.
                 */
-               if (join_unroller->inner_unroller[i] != NULL)
+               if (join_unroller->inner_unrollers[i] != NULL)
                        ujoin->inner[i].unrolled_join =
                                pgpa_build_unrolled_join(pstmt,
-                                                                                join_unroller->inner_unroller[k]);
+                                                                                join_unroller->inner_unrollers[k]);
                else
-                       ujoin->inner[i].rti = pgpa_scanrelid(ujoin->inner[i].plan);
-
-               /*
-                * If we need a clumped join object, build one.
-                */
-               if (pgpa_get_join_class(ujoin->inner[i].plan) == PGPA_CLUMPED_JOIN)
-                       ujoin->inner[i].clump_join =
-                               pgpa_build_clumped_join(ujoin->inner[i].plan);
-
-               /* XXX. What about elided nodes? */
+                       pgpa_fix_scan_or_clump_member(&ujoin->inner[i]);
        }
 
        return ujoin;
@@ -255,7 +293,8 @@ pgpa_destroy_join_unroller(pgpa_join_unroller *join_unroller)
 {
        pfree(join_unroller->strategy);
        pfree(join_unroller->inner_subplans);
-       pfree(join_unroller->inner_unroller);
+       pfree(join_unroller->inner_elided_nodes);
+       pfree(join_unroller->inner_unrollers);
        pfree(join_unroller);
 }
 
@@ -275,57 +314,125 @@ pgpa_destroy_join_unroller(pgpa_join_unroller *join_unroller)
  * may add additional nodes such as Materialize or Memoize. We regard these
  * as an aspect of the join strategy. As in the previous cases, the true input
  * to the join is the underlying node.
+ *
+ * However, if any involved child node previously had a now-elided node stacked
+ * on top, then we can't "look through" that node -- indeed, what's going to be
+ * relevant for our purposes is the ElidedNode on top of that plan node, rather
+ * than the plan node itself.
  */
 static pgpa_join_strategy
-pgpa_decompose_join(Plan *plan, Plan **realinner, Plan **realouter)
+pgpa_decompose_join(PlannedStmt *pstmt, Plan *plan,
+                                       Plan **realouter, Plan **realinner,
+                                       ElidedNode **elidedrealouter, ElidedNode **elidedrealinner)
 {
        Plan       *outerplan = plan->lefttree;
        Plan       *innerplan = plan->righttree;
+       ElidedNode *elidedouter;
+       ElidedNode *elidedinner;
        pgpa_join_strategy strategy;
 
+       elidedouter = pgpa_first_elided_node(pstmt, outerplan);
+       elidedinner = pgpa_first_elided_node(pstmt, innerplan);
+
        switch (nodeTag(plan))
        {
                case T_MergeJoin:
-                       if (IsA(innerplan, Sort))
-                               innerplan = innerplan->lefttree;
-                       if (IsA(outerplan, Sort))
+                       if (elidedouter == NULL && IsA(outerplan, Sort))
+                       {
                                outerplan = outerplan->lefttree;
-                       if (IsA(innerplan, Material))
+                               elidedouter = pgpa_first_elided_node(pstmt, outerplan);
+                       }
+
+                       if (elidedinner == NULL && IsA(innerplan, Sort))
                        {
                                innerplan = innerplan->lefttree;
+                               elidedinner = pgpa_first_elided_node(pstmt, innerplan);
+                       }
+
+                       if (elidedinner == NULL && IsA(innerplan, Material))
+                       {
+                               innerplan = innerplan->lefttree;
+                               elidedinner = pgpa_first_elided_node(pstmt, innerplan);
                                strategy = JSTRAT_MERGE_JOIN_MATERIALIZE;
                        }
                        else
                                strategy = JSTRAT_MERGE_JOIN_PLAIN;
                        break;
+
                case T_NestLoop:
-                       if (IsA(innerplan, Material))
+                       if (elidedinner == NULL && IsA(innerplan, Material))
                        {
                                innerplan = innerplan->lefttree;
+                               elidedinner = pgpa_first_elided_node(pstmt, innerplan);
                                strategy = JSTRAT_NESTED_LOOP_MATERIALIZE;
                        }
-                       else if (IsA(innerplan, Memoize))
+                       else if (elidedinner == NULL && IsA(innerplan, Memoize))
                        {
                                innerplan = innerplan->lefttree;
+                               elidedinner = pgpa_first_elided_node(pstmt, innerplan);
                                strategy = JSTRAT_NESTED_LOOP_MEMOIZE;
                        }
                        else
                                strategy = JSTRAT_NESTED_LOOP_PLAIN;
                        break;
+
                case T_HashJoin:
                        Assert(IsA(innerplan, Hash));
+                       Assert(elidedinner == NULL);
                        innerplan = innerplan->lefttree;
+                       elidedinner = pgpa_first_elided_node(pstmt, innerplan);
                        strategy = JSTRAT_HASH_JOIN;
                        break;
+
                default:
                        elog(ERROR, "unrecognized node type: %d", (int) nodeTag(plan));
        }
 
-       *realinner = innerplan;
        *realouter = outerplan;
+       *realinner = innerplan;
+       *elidedrealouter = elidedouter;
+       *elidedrealinner = elidedinner;
        return strategy;
 }
 
+/*
+ * Given a pgpa_join_member where the plan node and elided node, if any, have
+ * already been set, and where the plan node or elided node represents a
+ * clumped join or a scan, fill in the clump_join or rti field of the
+ * pgpa_join_member.
+ */
+static void
+pgpa_fix_scan_or_clump_member(pgpa_join_member *member)
+{
+       if (member->elided_node != NULL)
+       {
+               /*
+                * There is an elided node, so this is either a clumped join if the
+                * elided node mentions multiple relids or a scan if it only mentions
+                * one.
+                */
+               if (bms_membership(member->elided_node->relids) == BMS_MULTIPLE)
+                       member->clump_join =
+                               pgpa_build_clumped_join(member->plan,
+                                                                               member->elided_node);
+               else
+                       member->rti = bms_singleton_member(member->elided_node->relids);
+       }
+       else
+       {
+               /*
+                * There is no elided node, so we determine whether this is a clumped
+                * join or a scan by looking at the plan node itself.
+                */
+               if (pgpa_get_join_class(member->plan) == PGPA_CLUMPED_JOIN)
+                       member->clump_join =
+                               pgpa_build_clumped_join(member->plan,
+                                                                               member->elided_node);
+               else
+                       member->rti = pgpa_scanrelid(member->plan);
+       }
+}
+
 /*
  * Extract the scanned RTI from a plan node.
  *
@@ -354,12 +461,12 @@ pgpa_scanrelid(Plan *plan)
                        return ((Scan *) plan)->scanrelid;
                default:
                        {
-                               Bitmapset *relids = pgpa_relids(plan);
+                               Bitmapset  *relids = pgpa_relids(plan);
 
                                /*
-                                * If the node type is capable of carrying multiple relids
-                                * but the relids set actually present has exactly one member,
-                                * we regard it as a scan.
+                                * If the node type is capable of carrying multiple relids but
+                                * the relids set actually present has exactly one member, we
+                                * regard it as a scan.
                                 */
                                if (bms_membership(relids) == BMS_SINGLETON)
                                        return bms_singleton_member(relids);
@@ -390,9 +497,9 @@ pgpa_relids(Plan *plan)
 void
 pgpa_debug_out_clumped_join(StringInfo buf, pgpa_clumped_join *clump)
 {
-       char   *cstrategy;
-       int             rti = -1;
-       bool    first = true;
+       char       *cstrategy;
+       int                     rti = -1;
+       bool            first = true;
 
        cstrategy = pgpa_cstring_join_clump_strategy(clump->strategy);
        appendStringInfo(buf, "%s(", cstrategy);
@@ -418,7 +525,7 @@ pgpa_debug_out_unrolled_join(StringInfo buf, pgpa_unrolled_join *join)
 
        for (int k = 0; k < join->ninner; ++k)
        {
-               char   *cstrategy;
+               char       *cstrategy;
 
                cstrategy = pgpa_cstring_join_strategy(join->strategy[k]);
                appendStringInfo(buf, " %s ", cstrategy);
index f6161b7f4a19fd7d778f9112b1ca11df59a26b1e..37e351ee4ffc37577ae90be90f4d87984f737537 100644 (file)
@@ -132,7 +132,8 @@ typedef enum
 } pgpa_join_class;
 
 extern pgpa_join_class pgpa_get_join_class(Plan *plan);
-extern pgpa_clumped_join *pgpa_build_clumped_join(Plan *plan);
+extern pgpa_clumped_join *pgpa_build_clumped_join(Plan *plan,
+                                                                                                 ElidedNode *elided_node);
 
 extern pgpa_join_unroller *pgpa_create_join_unroller(void);
 extern void pgpa_unroll_join(PlannedStmt *pstmt, Plan *plan,
index 597ab90a4b14721755cadfae7c8c70d01c41a739..7d8736725c45216aa92bbe5f6c6ff1aac6d185e0 100644 (file)
@@ -21,9 +21,6 @@ pgpa_plan_walker(pgpa_plan_walker_context *context, Plan *plan,
                if (n->plan_node_id != plan->plan_node_id)
                        continue;
 
-               n->elided_type;
-               n->relids;      
-
                elog(WARNING, "look at me, i'm elided!");
        }
 
@@ -40,7 +37,7 @@ pgpa_plan_walker(pgpa_plan_walker_context *context, Plan *plan,
                {
                        pgpa_clumped_join *cjoin;
 
-                       cjoin = pgpa_build_clumped_join(plan);
+                       cjoin = pgpa_build_clumped_join(plan, NULL); /* XXX FIXME */
                        context->clumped_joins = lappend(context->clumped_joins, cjoin);
                }
        }
@@ -102,3 +99,15 @@ pgpa_plan_walker(pgpa_plan_walker_context *context, Plan *plan,
                pgpa_plan_walker(context, subplan, NULL);
        }
 }
+
+ElidedNode *
+pgpa_first_elided_node(PlannedStmt *pstmt, Plan *plan)
+{
+       foreach_node(ElidedNode, n, pstmt->elidedNodes)
+       {
+               if (n->plan_node_id == plan->plan_node_id)
+                       return n;
+       }
+
+       return NULL;
+}
index 353c9ebc8a633c85ce39e5c24a50d46c081aeba3..b8b0226e3de2e4d54341bd2a9fc911918154b9f0 100644 (file)
@@ -10,3 +10,5 @@ typedef struct pgpa_plan_walker_context
 extern void
 pgpa_plan_walker(pgpa_plan_walker_context *context, Plan *plan,
                                 pgpa_join_unroller *join_unroller);
+
+extern ElidedNode *pgpa_first_elided_node(PlannedStmt *pstmt, Plan *plan);