#include <limits.h>
+#include "access/htup_details.h"
#include "executor/executor.h"
#include "executor/nodeAgg.h"
#include "miscadmin.h"
/* 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);
glob = makeNode(PlannerGlobal);
glob->boundParams = boundParams;
- glob->paramlist = NIL;
glob->subplans = NIL;
glob->subroots = NIL;
glob->rewindPlanIDs = NULL;
glob->resultRelations = NIL;
glob->relationOids = NIL;
glob->invalItems = NIL;
+ glob->nParamExec = 0;
glob->lastPHId = 0;
glob->lastRowMarkId = 0;
glob->transientPlan = false;
result = makeNode(PlannedStmt);
result->commandType = parse->commandType;
+ result->queryId = parse->queryId;
result->hasReturning = (parse->returningList != NIL);
result->hasModifyingCTE = parse->hasModifyingCTE;
result->canSetTag = parse->canSetTag;
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;
}
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;
* 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
/*
* 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)
{
{
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;
}
/*
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);
+ }
}
/*
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;
* 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 */
* 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)
/*
* 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);
/*
(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
* 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);
/*
* 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)
{
if (rte->rtekind == RTE_SUBQUERY)
{
- Index newrti;
+ Index newrti;
/*
* The RTE can't contain any references to its own RT
}
/* 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;
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
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) */
double sub_limit_tuples;
AttrNumber *groupColIdx = NULL;
bool need_tlist_eval = true;
- QualCost tlist_cost;
Path *cheapest_path;
Path *sorted_path;
Path *best_path;
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)
{
/*
* 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
{
*
* 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.
*/
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.
}
/*
- * 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;
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);
* 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,
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);
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);
/* 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);
}