On Wed, 27 Mar 2024 at 22:47, David Rowley <dgrowle...@gmail.com> wrote:
> I did wonder when first working on this patch if subquery_planner()
> should grow an extra parameter, or maybe consolidate some existing
> ones by passing some struct that provides the planner with a bit more
> context about the query.  A few of the existing parameters are likely
> candidates for being in such a struct. e.g. hasRecursion and
> tuple_fraction. A SetOperationStmt could go in there too.

The attached is roughly what I had in mind.  I've not taken the time
to see what comments need to be updated, so the attached aims only to
assist discussion.

David
diff --git a/src/backend/optimizer/path/allpaths.c 
b/src/backend/optimizer/path/allpaths.c
index 7bad404458..26cbe2c9a2 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -2494,6 +2494,7 @@ static void
 set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
                                          Index rti, RangeTblEntry *rte)
 {
+       PlannerContext context;
        Query      *parse = root->parse;
        Query      *subquery = rte->subquery;
        bool            trivial_pathtarget;
@@ -2644,10 +2645,12 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo 
*rel,
        /* plan_params should not be in use in current query level */
        Assert(root->plan_params == NIL);
 
+       context.hasRecursion = false;
+       context.tuple_fraction = tuple_fraction;
+       context.setops = NULL;
+
        /* Generate a subroot and Paths for the subquery */
-       rel->subroot = subquery_planner(root->glob, subquery,
-                                                                       root,
-                                                                       false, 
tuple_fraction);
+       rel->subroot = subquery_planner(root->glob, subquery, root, &context);
 
        /* Isolate the params needed by this specific subplan */
        rel->subplan_params = root->plan_params;
diff --git a/src/backend/optimizer/plan/planner.c 
b/src/backend/optimizer/plan/planner.c
index 38d070fa00..56fa445ae8 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -127,7 +127,7 @@ typedef struct
 /* Local functions */
 static Node *preprocess_expression(PlannerInfo *root, Node *expr, int kind);
 static void preprocess_qual_conditions(PlannerInfo *root, Node *jtnode);
-static void grouping_planner(PlannerInfo *root, double tuple_fraction);
+static void grouping_planner(PlannerInfo *root, PlannerContext *context);
 static grouping_sets_data *preprocess_grouping_sets(PlannerInfo *root);
 static List *remap_to_groupclause_idx(List *groupClause, List *gsets,
                                                                          int 
*tleref_to_colnum_map);
@@ -288,6 +288,7 @@ standard_planner(Query *parse, const char *query_string, 
int cursorOptions,
 {
        PlannedStmt *result;
        PlannerGlobal *glob;
+       PlannerContext context;
        double          tuple_fraction;
        PlannerInfo *root;
        RelOptInfo *final_rel;
@@ -410,9 +411,12 @@ standard_planner(Query *parse, const char *query_string, 
int cursorOptions,
                tuple_fraction = 0.0;
        }
 
+       context.hasRecursion = false;
+       context.tuple_fraction = tuple_fraction;
+       context.setops = NULL;
+
        /* primary planning entry point (may recurse for subqueries) */
-       root = subquery_planner(glob, parse, NULL,
-                                                       false, tuple_fraction);
+       root = subquery_planner(glob, parse, NULL, &context);
 
        /* Select best Path and turn it into a Plan */
        final_rel = fetch_upper_rel(root, UPPERREL_FINAL, NULL);
@@ -600,9 +604,6 @@ standard_planner(Query *parse, const char *query_string, 
int cursorOptions,
  * glob is the global state for the current planner run.
  * parse is the querytree produced by the parser & rewriter.
  * parent_root is the immediate parent Query's info (NULL at the top level).
- * 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.
  *
  * Basically, this routine does the stuff that should only be done once
  * per Query object.  It then calls grouping_planner.  At one time,
@@ -623,7 +624,7 @@ standard_planner(Query *parse, const char *query_string, 
int cursorOptions,
 PlannerInfo *
 subquery_planner(PlannerGlobal *glob, Query *parse,
                                 PlannerInfo *parent_root,
-                                bool hasRecursion, double tuple_fraction)
+                                PlannerContext *context)
 {
        PlannerInfo *root;
        List       *newWithCheckOptions;
@@ -667,8 +668,8 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
        root->hasPseudoConstantQuals = false;
        root->hasAlternativeSubPlans = false;
        root->placeholdersFrozen = false;
-       root->hasRecursion = hasRecursion;
-       if (hasRecursion)
+       root->hasRecursion = context->hasRecursion;
+       if (context->hasRecursion)
                root->wt_param_id = assign_special_exec_param(root);
        else
                root->wt_param_id = -1;
@@ -1082,7 +1083,7 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
        /*
         * Do the main planning.
         */
-       grouping_planner(root, tuple_fraction);
+       grouping_planner(root, context);
 
        /*
         * Capture the set of outer-level param IDs we have access to, for use 
in
@@ -1274,14 +1275,6 @@ preprocess_phv_expression(PlannerInfo *root, Expr *expr)
  * This function adds all required top-level processing to the scan/join
  * Path(s) produced by query_planner.
  *
- * tuple_fraction is the fraction of tuples we expect will be retrieved.
- * tuple_fraction is interpreted as follows:
- *       0: expect all tuples to be retrieved (normal case)
- *       0 < tuple_fraction < 1: expect the given fraction of tuples available
- *             from the plan to be retrieved
- *       tuple_fraction >= 1: tuple_fraction is the absolute number of tuples
- *             expected to be retrieved (ie, a LIMIT specification)
- *
  * Returns nothing; the useful output is in the Paths we attach to the
  * (UPPERREL_FINAL, NULL) upperrel in *root.  In addition,
  * root->processed_tlist contains the final processed targetlist.
@@ -1291,7 +1284,7 @@ preprocess_phv_expression(PlannerInfo *root, Expr *expr)
  *--------------------
  */
 static void
-grouping_planner(PlannerInfo *root, double tuple_fraction)
+grouping_planner(PlannerInfo *root, PlannerContext *context)
 {
        Query      *parse = root->parse;
        int64           offset_est = 0;
@@ -1305,6 +1298,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
        RelOptInfo *current_rel;
        RelOptInfo *final_rel;
        FinalPathExtraData extra;
+       double          tuple_fraction = context->tuple_fraction;
        ListCell   *lc;
 
        /* Tweak caller-supplied tuple_fraction if have LIMIT/OFFSET */
@@ -1507,16 +1501,10 @@ grouping_planner(PlannerInfo *root, double 
tuple_fraction)
                qp_extra.gset_data = gset_data;
 
                /*
-                * Check if we're a subquery for a set operation.  If we are, 
store
-                * the SetOperationStmt in qp_extra.
+                * If we're a subquery for a set operation, store the 
SetOperationStmt in
+                * qp_extra.
                 */
-               if (root->parent_root != NULL &&
-                       root->parent_root->parse->setOperations != NULL &&
-                       IsA(root->parent_root->parse->setOperations, 
SetOperationStmt))
-                       qp_extra.setop =
-                               (SetOperationStmt *) 
root->parent_root->parse->setOperations;
-               else
-                       qp_extra.setop = NULL;
+               qp_extra.setop = (SetOperationStmt *) context->setops;
 
                /*
                 * Generate the best unsorted and presorted paths for the 
scan/join
diff --git a/src/backend/optimizer/plan/subselect.c 
b/src/backend/optimizer/plan/subselect.c
index d5fa281b10..49be10e29b 100644
--- a/src/backend/optimizer/plan/subselect.c
+++ b/src/backend/optimizer/plan/subselect.c
@@ -167,6 +167,7 @@ make_subplan(PlannerInfo *root, Query *orig_subquery,
        bool            simple_exists = false;
        double          tuple_fraction;
        PlannerInfo *subroot;
+       PlannerContext context;
        RelOptInfo *final_rel;
        Path       *best_path;
        Plan       *plan;
@@ -217,10 +218,12 @@ make_subplan(PlannerInfo *root, Query *orig_subquery,
        /* plan_params should not be in use in current query level */
        Assert(root->plan_params == NIL);
 
+       context.hasRecursion = false;
+       context.tuple_fraction = tuple_fraction;
+       context.setops = NULL;
+
        /* Generate Paths for the subquery */
-       subroot = subquery_planner(root->glob, subquery,
-                                                          root,
-                                                          false, 
tuple_fraction);
+       subroot = subquery_planner(root->glob, subquery, root, &context);
 
        /* Isolate the params needed by this specific subplan */
        plan_params = root->plan_params;
@@ -265,10 +268,14 @@ make_subplan(PlannerInfo *root, Query *orig_subquery,
                                                                                
 &newtestexpr, &paramIds);
                if (subquery)
                {
+                       PlannerContext context;
+
+                       context.hasRecursion = false;
+                       context.tuple_fraction = 0.0;
+                       context.setops = NULL;
+
                        /* Generate Paths for the ANY subquery; we'll need all 
rows */
-                       subroot = subquery_planner(root->glob, subquery,
-                                                                          root,
-                                                                          
false, 0.0);
+                       subroot = subquery_planner(root->glob, subquery, root, 
&context);
 
                        /* Isolate the params needed by this specific subplan */
                        plan_params = root->plan_params;
@@ -891,6 +898,7 @@ SS_process_ctes(PlannerInfo *root)
                CmdType         cmdType = ((Query *) 
cte->ctequery)->commandType;
                Query      *subquery;
                PlannerInfo *subroot;
+               PlannerContext context;
                RelOptInfo *final_rel;
                Path       *best_path;
                Plan       *plan;
@@ -963,13 +971,16 @@ SS_process_ctes(PlannerInfo *root)
                /* plan_params should not be in use in current query level */
                Assert(root->plan_params == NIL);
 
+               context.hasRecursion = cte->cterecursive;
                /*
-                * Generate Paths for the CTE query.  Always plan for full 
retrieval
-                * --- we don't have enough info to predict otherwise.
+                * Always plan for full retrieval --- we don't have enough info 
to
+                * predict otherwise.
                 */
-               subroot = subquery_planner(root->glob, subquery,
-                                                                  root,
-                                                                  
cte->cterecursive, 0.0);
+               context.tuple_fraction = 0.0;
+               context.setops = NULL;
+
+               /* Generate Paths for the CTE query. */
+               subroot = subquery_planner(root->glob, subquery, root, 
&context);
 
                /*
                 * Since the current query level doesn't yet contain any RTEs, 
it
diff --git a/src/backend/optimizer/prep/prepunion.c 
b/src/backend/optimizer/prep/prepunion.c
index 944afc7192..113c721602 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -242,6 +242,7 @@ recurse_set_operations(Node *setOp, PlannerInfo *root,
 
        if (IsA(setOp, RangeTblRef))
        {
+               PlannerContext context;
                RangeTblRef *rtr = (RangeTblRef *) setOp;
                RangeTblEntry *rte = root->simple_rte_array[rtr->rtindex];
                Query      *subquery = rte->subquery;
@@ -257,11 +258,14 @@ recurse_set_operations(Node *setOp, PlannerInfo *root,
                /* plan_params should not be in use in current query level */
                Assert(root->plan_params == NIL);
 
+               context.hasRecursion = false;
+               context.tuple_fraction = root->tuple_fraction;
+               context.setops = castNode(SetOperationStmt,
+                                                                 
root->parse->setOperations);
+
                /* Generate a subroot and Paths for the subquery */
-               subroot = rel->subroot = subquery_planner(root->glob, subquery,
-                                                                               
                  root,
-                                                                               
                  false,
-                                                                               
                  root->tuple_fraction);
+               subroot = rel->subroot = subquery_planner(root->glob, subquery, 
root,
+                                                                               
                  &context);
 
                /*
                 * It should not be possible for the primitive query to contain 
any
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 595eec2cbb..a3727ba2f1 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -551,6 +551,37 @@ struct PlannerInfo
        bool            partColsUpdated;
 };
 
+/*----------
+ * PlannerContext
+ *             Per-query context to provide additional information to
+ *             subquery_planner() and grouping_planner().
+ */
+typedef struct PlannerContext
+{
+       /*
+        * hasRecursion: must be given as true if planning recursive WITH query.
+        */
+       bool hasRecursion;
+
+       /*--------------------
+        * tuple_fraction is the fraction of tuples we expect will be retrieved.
+        * tuple_fraction is interpreted as follows:
+        *        0: expect all tuples to be retrieved (normal case)
+        *        0 < tuple_fraction < 1: expect the given fraction of tuples 
available
+        *              from the plan to be retrieved
+        *        tuple_fraction >= 1: tuple_fraction is the absolute number of 
tuples
+        *              expected to be retrieved (ie, a LIMIT specification)
+        *--------------------
+        */
+       double tuple_fraction;
+
+       /*
+        * Can be set for set operation child queries to provide information on
+        * the parent-level operation to allow them to optimize for this context
+        */
+       SetOperationStmt *setops;
+
+} PlannerContext;
 
 /*
  * In places where it's known that simple_rte_array[] must have been prepared
diff --git a/src/include/optimizer/planner.h b/src/include/optimizer/planner.h
index e1d79ffdf3..e3c9a4e3ae 100644
--- a/src/include/optimizer/planner.h
+++ b/src/include/optimizer/planner.h
@@ -44,7 +44,7 @@ extern PlannedStmt *standard_planner(Query *parse, const char 
*query_string,
 
 extern PlannerInfo *subquery_planner(PlannerGlobal *glob, Query *parse,
                                                                         
PlannerInfo *parent_root,
-                                                                        bool 
hasRecursion, double tuple_fraction);
+                                                                        
PlannerContext *context);
 
 extern RowMarkType select_rowmark_type(RangeTblEntry *rte,
                                                                           
LockClauseStrength strength);

Reply via email to