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, ¶mIds); 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);