X-Git-Url: https://granicus.if.org/sourcecode?a=blobdiff_plain;f=src%2Fbackend%2Foptimizer%2Fplan%2Fplanner.c;h=385e64647ea0dea00c15889064421a01ad2dab79;hb=46c508fbcf98ac334f1e831d21021d731c882fbb;hp=b66a508c22da934e96771c99a0a5010dc6c64177;hpb=e126958c2efdfc2d60b978d1fc7a780ff647e8ad;p=postgresql diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index b66a508c22..385e64647e 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -17,6 +17,7 @@ #include +#include "access/htup_details.h" #include "executor/executor.h" #include "executor/nodeAgg.h" #include "miscadmin.h" @@ -48,12 +49,15 @@ planner_hook_type planner_hook = NULL; /* Expression kind codes for preprocess_expression */ -#define EXPRKIND_QUAL 0 -#define EXPRKIND_TARGET 1 -#define EXPRKIND_RTFUNC 2 -#define EXPRKIND_VALUES 3 -#define EXPRKIND_LIMIT 4 -#define EXPRKIND_APPINFO 5 +#define EXPRKIND_QUAL 0 +#define EXPRKIND_TARGET 1 +#define EXPRKIND_RTFUNC 2 +#define EXPRKIND_RTFUNC_LATERAL 3 +#define EXPRKIND_VALUES 4 +#define EXPRKIND_VALUES_LATERAL 5 +#define EXPRKIND_LIMIT 6 +#define EXPRKIND_APPINFO 7 +#define EXPRKIND_PHV 8 static Node *preprocess_expression(PlannerInfo *root, Node *expr, int kind); @@ -151,7 +155,6 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams) glob = makeNode(PlannerGlobal); glob->boundParams = boundParams; - glob->paramlist = NIL; glob->subplans = NIL; glob->subroots = NIL; glob->rewindPlanIDs = NULL; @@ -160,6 +163,7 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams) glob->resultRelations = NIL; glob->relationOids = NIL; glob->invalItems = NIL; + glob->nParamExec = 0; glob->lastPHId = 0; glob->lastRowMarkId = 0; glob->transientPlan = false; @@ -225,6 +229,7 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams) result = makeNode(PlannedStmt); result->commandType = parse->commandType; + result->queryId = parse->queryId; result->hasReturning = (parse->returningList != NIL); result->hasModifyingCTE = parse->hasModifyingCTE; result->canSetTag = parse->canSetTag; @@ -233,13 +238,12 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams) result->rtable = glob->finalrtable; result->resultRelations = glob->resultRelations; result->utilityStmt = parse->utilityStmt; - result->intoClause = parse->intoClause; result->subplans = glob->subplans; result->rewindPlanIDs = glob->rewindPlanIDs; result->rowMarks = glob->finalrowmarks; result->relationOids = glob->relationOids; result->invalItems = glob->invalItems; - result->nParamExec = list_length(glob->paramlist); + result->nParamExec = glob->nParamExec; return result; } @@ -291,6 +295,7 @@ subquery_planner(PlannerGlobal *glob, Query *parse, root->glob = glob; root->query_level = parent_root ? parent_root->query_level + 1 : 1; root->parent_root = parent_root; + root->plan_params = NIL; root->planner_cxt = CurrentMemoryContext; root->init_plans = NIL; root->cte_plan_ids = NIL; @@ -334,7 +339,7 @@ subquery_planner(PlannerGlobal *glob, Query *parse, * query. */ parse->jointree = (FromExpr *) - pull_up_subqueries(root, (Node *) parse->jointree, NULL, NULL); + pull_up_subqueries(root, (Node *) parse->jointree); /* * If this is a simple UNION ALL query, flatten it into an appendrel. We @@ -348,10 +353,12 @@ subquery_planner(PlannerGlobal *glob, Query *parse, /* * Detect whether any rangetable entries are RTE_JOIN kind; if not, we can * avoid the expense of doing flatten_join_alias_vars(). Also check for - * outer joins --- if none, we can skip reduce_outer_joins(). This must be - * done after we have done pull_up_subqueries, of course. + * outer joins --- if none, we can skip reduce_outer_joins(). And check + * for LATERAL RTEs, too. This must be done after we have done + * pull_up_subqueries(), of course. */ root->hasJoinRTEs = false; + root->hasLateralRTEs = false; hasOuterJoins = false; foreach(l, parse->rtable) { @@ -361,12 +368,10 @@ subquery_planner(PlannerGlobal *glob, Query *parse, { root->hasJoinRTEs = true; if (IS_OUTER_JOIN(rte->jointype)) - { hasOuterJoins = true; - /* Can quit scanning once we find an outer join */ - break; - } } + if (rte->lateral) + root->hasLateralRTEs = true; } /* @@ -436,18 +441,38 @@ subquery_planner(PlannerGlobal *glob, Query *parse, preprocess_expression(root, (Node *) root->append_rel_list, EXPRKIND_APPINFO); - /* Also need to preprocess expressions for function and values RTEs */ + /* Also need to preprocess expressions within RTEs */ foreach(l, parse->rtable) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); + int kind; - if (rte->rtekind == RTE_FUNCTION) - rte->funcexpr = preprocess_expression(root, rte->funcexpr, - EXPRKIND_RTFUNC); + if (rte->rtekind == RTE_SUBQUERY) + { + /* + * We don't want to do all preprocessing yet on the subquery's + * expressions, since that will happen when we plan it. But if it + * contains any join aliases of our level, those have to get + * expanded now, because planning of the subquery won't do it. + * That's only possible if the subquery is LATERAL. + */ + if (rte->lateral && root->hasJoinRTEs) + rte->subquery = (Query *) + flatten_join_alias_vars(root, (Node *) rte->subquery); + } + else if (rte->rtekind == RTE_FUNCTION) + { + /* Preprocess the function expression fully */ + kind = rte->lateral ? EXPRKIND_RTFUNC_LATERAL : EXPRKIND_RTFUNC; + rte->funcexpr = preprocess_expression(root, rte->funcexpr, kind); + } else if (rte->rtekind == RTE_VALUES) + { + /* Preprocess the values lists fully */ + kind = rte->lateral ? EXPRKIND_VALUES_LATERAL : EXPRKIND_VALUES; rte->values_lists = (List *) - preprocess_expression(root, (Node *) rte->values_lists, - EXPRKIND_VALUES); + preprocess_expression(root, (Node *) rte->values_lists, kind); + } } /* @@ -529,22 +554,10 @@ subquery_planner(PlannerGlobal *glob, Query *parse, List *rowMarks; /* - * Deal with the RETURNING clause if any. It's convenient to pass - * the returningList through setrefs.c now rather than at top - * level (if we waited, handling inherited UPDATE/DELETE would be - * much harder). + * Set up the RETURNING list-of-lists, if needed. */ if (parse->returningList) - { - List *rlist; - - Assert(parse->resultRelation); - rlist = set_returning_clause_references(root, - parse->returningList, - plan, - parse->resultRelation); - returningLists = list_make1(rlist); - } + returningLists = list_make1(parse->returningList); else returningLists = NIL; @@ -574,7 +587,7 @@ subquery_planner(PlannerGlobal *glob, Query *parse, * and attach the initPlans to the top plan node. */ if (list_length(glob->subplans) != num_old_subplans || - root->glob->paramlist != NIL) + root->glob->nParamExec > 0) SS_finalize_plan(root, plan, true); /* Return internal info if caller wants it */ @@ -588,7 +601,7 @@ subquery_planner(PlannerGlobal *glob, Query *parse, * preprocess_expression * Do subquery_planner's preprocessing work for an expression, * which can be a targetlist, a WHERE clause (including JOIN/ON - * conditions), or a HAVING clause. + * conditions), a HAVING clause, or a few other things. */ static Node * preprocess_expression(PlannerInfo *root, Node *expr, int kind) @@ -603,12 +616,13 @@ preprocess_expression(PlannerInfo *root, Node *expr, int kind) /* * If the query has any join RTEs, replace join alias variables with - * base-relation variables. We must do this before sublink processing, - * else sublinks expanded out from join aliases wouldn't get processed. We - * can skip it in VALUES lists, however, since they can't contain any Vars - * at all. + * base-relation variables. We must do this before sublink processing, + * else sublinks expanded out from join aliases would not get processed. + * We can skip it in non-lateral RTE functions and VALUES lists, however, + * since they can't contain any Vars of the current query level. */ - if (root->hasJoinRTEs && kind != EXPRKIND_VALUES) + if (root->hasJoinRTEs && + !(kind == EXPRKIND_RTFUNC || kind == EXPRKIND_VALUES)) expr = flatten_join_alias_vars(root, expr); /* @@ -704,6 +718,23 @@ preprocess_qual_conditions(PlannerInfo *root, Node *jtnode) (int) nodeTag(jtnode)); } +/* + * preprocess_phv_expression + * Do preprocessing on a PlaceHolderVar expression that's been pulled up. + * + * If a LATERAL subquery references an output of another subquery, and that + * output must be wrapped in a PlaceHolderVar because of an intermediate outer + * join, then we'll push the PlaceHolderVar expression down into the subquery + * and later pull it back up during find_lateral_references, which runs after + * subquery_planner has preprocessed all the expressions that were in the + * current query level to start with. So we need to preprocess it then. + */ +Expr * +preprocess_phv_expression(PlannerInfo *root, Expr *expr) +{ + return (Expr *) preprocess_expression(root, (Node *) expr, EXPRKIND_PHV); +} + /* * inheritance_planner * Generate a plan in the case where the result relation is an @@ -772,14 +803,15 @@ inheritance_planner(PlannerInfo *root) * then fool around with subquery RTEs. */ subroot.parse = (Query *) - adjust_appendrel_attrs((Node *) parse, + adjust_appendrel_attrs(root, + (Node *) parse, appinfo); /* * The rowMarks list might contain references to subquery RTEs, so - * make a copy that we can apply ChangeVarNodes to. (Fortunately, - * the executor doesn't need to see the modified copies --- we can - * just pass it the original rowMarks list.) + * make a copy that we can apply ChangeVarNodes to. (Fortunately, the + * executor doesn't need to see the modified copies --- we can just + * pass it the original rowMarks list.) */ subroot.rowMarks = (List *) copyObject(root->rowMarks); @@ -795,10 +827,11 @@ inheritance_planner(PlannerInfo *root) /* * If this isn't the first child Query, generate duplicates of all - * subquery RTEs, and adjust Var numbering to reference the duplicates. - * To simplify the loop logic, we scan the original rtable not the - * copy just made by adjust_appendrel_attrs; that should be OK since - * subquery RTEs couldn't contain any references to the target rel. + * subquery RTEs, and adjust Var numbering to reference the + * duplicates. To simplify the loop logic, we scan the original rtable + * not the copy just made by adjust_appendrel_attrs; that should be OK + * since subquery RTEs couldn't contain any references to the target + * rel. */ if (final_rtable != NIL) { @@ -811,7 +844,7 @@ inheritance_planner(PlannerInfo *root) if (rte->rtekind == RTE_SUBQUERY) { - Index newrti; + Index newrti; /* * The RTE can't contain any references to its own RT @@ -831,12 +864,11 @@ inheritance_planner(PlannerInfo *root) } /* We needn't modify the child's append_rel_list */ - /* There shouldn't be any OJ info to translate, as yet */ + /* There shouldn't be any OJ or LATERAL info to translate, as yet */ Assert(subroot.join_info_list == NIL); + Assert(subroot.lateral_info_list == NIL); /* and we haven't created PlaceHolderInfos, either */ Assert(subroot.placeholder_list == NIL); - /* build a separate list of initplans for each child */ - subroot.init_plans = NIL; /* hack to mark target relation as an inheritance partition */ subroot.hasInheritedTarget = true; @@ -862,7 +894,7 @@ inheritance_planner(PlannerInfo *root) else final_rtable = list_concat(final_rtable, list_copy_tail(subroot.parse->rtable, - list_length(final_rtable))); + list_length(final_rtable))); /* * We need to collect all the RelOptInfos from all child plans into @@ -883,22 +915,15 @@ inheritance_planner(PlannerInfo *root) save_rel_array = subroot.simple_rel_array; /* Make sure any initplans from this rel get into the outer list */ - root->init_plans = list_concat(root->init_plans, subroot.init_plans); + root->init_plans = subroot.init_plans; /* Build list of target-relation RT indexes */ resultRelations = lappend_int(resultRelations, appinfo->child_relid); /* Build list of per-relation RETURNING targetlists */ if (parse->returningList) - { - List *rlist; - - rlist = set_returning_clause_references(&subroot, - subroot.parse->returningList, - subplan, - appinfo->child_relid); - returningLists = lappend(returningLists, rlist); - } + returningLists = lappend(returningLists, + subroot.parse->returningList); } /* Mark result as unordered (probably unnecessary) */ @@ -1064,7 +1089,6 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) double sub_limit_tuples; AttrNumber *groupColIdx = NULL; bool need_tlist_eval = true; - QualCost tlist_cost; Path *cheapest_path; Path *sorted_path; Path *best_path; @@ -1337,18 +1361,17 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) need_sort_for_grouping = true; /* - * Always override create_plan's tlist, so that we don't - * sort useless data from a "physical" tlist. + * Always override create_plan's tlist, so that we don't sort + * useless data from a "physical" tlist. */ need_tlist_eval = true; } /* - * create_plan returns a plan with just a "flat" tlist of - * required Vars. Usually we need to insert the sub_tlist as the - * tlist of the top plan node. However, we can skip that if we - * determined that whatever create_plan chose to return will be - * good enough. + * create_plan returns a plan with just a "flat" tlist of required + * Vars. Usually we need to insert the sub_tlist as the tlist of + * the top plan node. However, we can skip that if we determined + * that whatever create_plan chose to return will be good enough. */ if (need_tlist_eval) { @@ -1375,27 +1398,9 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) /* * Also, account for the cost of evaluation of the sub_tlist. - * - * Up to now, we have only been dealing with "flat" tlists, - * containing just Vars. So their evaluation cost is zero - * according to the model used by cost_qual_eval() (or if you - * prefer, the cost is factored into cpu_tuple_cost). Thus we - * can avoid accounting for tlist cost throughout - * query_planner() and subroutines. But now we've inserted a - * tlist that might contain actual operators, sub-selects, etc - * --- so we'd better account for its cost. - * - * Below this point, any tlist eval cost for added-on nodes - * should be accounted for as we create those nodes. - * Presently, of the node types we can add on, only Agg, - * WindowAgg, and Group project new tlists (the rest just copy - * their input tuples) --- so make_agg(), make_windowagg() and - * make_group() are responsible for computing the added cost. + * See comments for add_tlist_costs_to_plan() for more info. */ - cost_qual_eval(&tlist_cost, sub_tlist, root); - result_plan->startup_cost += tlist_cost.startup; - result_plan->total_cost += tlist_cost.startup + - tlist_cost.per_tuple * result_plan->plan_rows; + add_tlist_costs_to_plan(root, result_plan, sub_tlist); } else { @@ -1566,7 +1571,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) * * Note: it's essential here to use PVC_INCLUDE_AGGREGATES so that * Vars mentioned only in aggregate expressions aren't pulled out - * as separate targetlist entries. Otherwise we could be putting + * as separate targetlist entries. Otherwise we could be putting * ungrouped Vars directly into an Agg node's tlist, resulting in * undefined behavior. */ @@ -1835,6 +1840,61 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) return result_plan; } +/* + * add_tlist_costs_to_plan + * + * Estimate the execution costs associated with evaluating the targetlist + * expressions, and add them to the cost estimates for the Plan node. + * + * If the tlist contains set-returning functions, also inflate the Plan's cost + * and plan_rows estimates accordingly. (Hence, this must be called *after* + * any logic that uses plan_rows to, eg, estimate qual evaluation costs.) + * + * Note: during initial stages of planning, we mostly consider plan nodes with + * "flat" tlists, containing just Vars. So their evaluation cost is zero + * according to the model used by cost_qual_eval() (or if you prefer, the cost + * is factored into cpu_tuple_cost). Thus we can avoid accounting for tlist + * cost throughout query_planner() and subroutines. But once we apply a + * tlist that might contain actual operators, sub-selects, etc, we'd better + * account for its cost. Any set-returning functions in the tlist must also + * affect the estimated rowcount. + * + * Once grouping_planner() has applied a general tlist to the topmost + * scan/join plan node, any tlist eval cost for added-on nodes should be + * accounted for as we create those nodes. Presently, of the node types we + * can add on later, only Agg, WindowAgg, and Group project new tlists (the + * rest just copy their input tuples) --- so make_agg(), make_windowagg() and + * make_group() are responsible for calling this function to account for their + * tlist costs. + */ +void +add_tlist_costs_to_plan(PlannerInfo *root, Plan *plan, List *tlist) +{ + QualCost tlist_cost; + double tlist_rows; + + cost_qual_eval(&tlist_cost, tlist, root); + plan->startup_cost += tlist_cost.startup; + plan->total_cost += tlist_cost.startup + + tlist_cost.per_tuple * plan->plan_rows; + + tlist_rows = tlist_returns_set_rows(tlist); + if (tlist_rows > 1) + { + /* + * We assume that execution costs of the tlist proper were all + * accounted for by cost_qual_eval. However, it still seems + * appropriate to charge something more for the executor's general + * costs of processing the added tuples. The cost is probably less + * than cpu_tuple_cost, though, so we arbitrarily use half of that. + */ + plan->total_cost += plan->plan_rows * (tlist_rows - 1) * + cpu_tuple_cost / 2; + + plan->plan_rows *= tlist_rows; + } +} + /* * Detect whether a plan node is a "dummy" plan created when a relation * is deemed not to need scanning due to constraint exclusion. @@ -2673,8 +2733,8 @@ make_subplanTargetList(PlannerInfo *root, } /* - * Otherwise, we must build a tlist containing all grouping columns, - * plus any other Vars mentioned in the targetlist and HAVING qual. + * Otherwise, we must build a tlist containing all grouping columns, plus + * any other Vars mentioned in the targetlist and HAVING qual. */ sub_tlist = NIL; non_group_cols = NIL; @@ -2725,8 +2785,8 @@ make_subplanTargetList(PlannerInfo *root, else { /* - * Non-grouping column, so just remember the expression - * for later call to pull_var_clause. There's no need for + * Non-grouping column, so just remember the expression for + * later call to pull_var_clause. There's no need for * pull_var_clause to examine the TargetEntry node itself. */ non_group_cols = lappend(non_group_cols, tle->expr); @@ -2753,7 +2813,7 @@ make_subplanTargetList(PlannerInfo *root, * add them to the result tlist if not already present. (A Var used * directly as a GROUP BY item will be present already.) Note this * includes Vars used in resjunk items, so we are covering the needs of - * ORDER BY and window specifications. Vars used within Aggrefs will be + * ORDER BY and window specifications. Vars used within Aggrefs will be * pulled out here, too. */ non_group_vars = pull_var_clause((Node *) non_group_cols, @@ -3241,6 +3301,7 @@ plan_cluster_use_sort(Oid tableOid, Oid indexOid) rte->rtekind = RTE_RELATION; rte->relid = tableOid; rte->relkind = RELKIND_RELATION; + rte->lateral = false; rte->inh = false; rte->inFromCl = true; query->rtable = list_make1(rte); @@ -3289,7 +3350,7 @@ plan_cluster_use_sort(Oid tableOid, Oid indexOid) comparisonCost = 2.0 * (indexExprCost.startup + indexExprCost.per_tuple); /* Estimate the cost of seq scan + sort */ - seqScanPath = create_seqscan_path(root, rel); + seqScanPath = create_seqscan_path(root, rel, NULL); cost_sort(&seqScanAndSortPath, root, NIL, seqScanPath->total_cost, rel->tuples, rel->width, comparisonCost, maintenance_work_mem, -1.0); @@ -3297,7 +3358,8 @@ plan_cluster_use_sort(Oid tableOid, Oid indexOid) /* Estimate the cost of index scan */ indexScanPath = create_index_path(root, indexInfo, NIL, NIL, NIL, NIL, NIL, - ForwardScanDirection, false, NULL); + ForwardScanDirection, false, + NULL, 1.0); return (seqScanAndSortPath.total_cost < indexScanPath->path.total_cost); }