aboutsummaryrefslogtreecommitdiff
path: root/src/backend/optimizer/plan/createplan.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2013-08-17 20:22:41 -0400
committerTom Lane <tgl@sss.pgh.pa.us>2013-08-17 20:22:41 -0400
commit517db4945560358a82b9152d01cfad3bbd2af17e (patch)
treeebc6a2807696fdb860aec87e19b806430500a4b3 /src/backend/optimizer/plan/createplan.c
parent2505aaed7be290018f999f6e9250c24b26db97d0 (diff)
downloadpostgresql-517db4945560358a82b9152d01cfad3bbd2af17e.tar.gz
postgresql-517db4945560358a82b9152d01cfad3bbd2af17e.zip
Fix planner problems with LATERAL references in PlaceHolderVars.
The planner largely failed to consider the possibility that a PlaceHolderVar's expression might contain a lateral reference to a Var coming from somewhere outside the PHV's syntactic scope. We had a previous report of a problem in this area, which I tried to fix in a quick-hack way in commit 4da6439bd8553059766011e2a42c6e39df08717f, but Antonin Houska pointed out that there were still some problems, and investigation turned up other issues. This patch largely reverts that commit in favor of a more thoroughly thought-through solution. The new theory is that a PHV's ph_eval_at level cannot be higher than its original syntactic level. If it contains lateral references, those don't change the ph_eval_at level, but rather they create a lateral-reference requirement for the ph_eval_at join relation. The code in joinpath.c needs to handle that. Another issue is that createplan.c wasn't handling nested PlaceHolderVars properly. In passing, push knowledge of lateral-reference checks for join clauses into join_clause_is_movable_to. This is mainly so that FDWs don't need to deal with it. This patch doesn't fix the original join-qual-placement problem reported by Jeremy Evans (and indeed, one of the new regression test cases shows the wrong answer because of that). But the PlaceHolderVar problems need to be fixed before that issue can be addressed, so committing this separately seems reasonable.
Diffstat (limited to 'src/backend/optimizer/plan/createplan.c')
-rw-r--r--src/backend/optimizer/plan/createplan.c100
1 files changed, 61 insertions, 39 deletions
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 52bab79007e..c501737a267 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -44,9 +44,9 @@
static Plan *create_plan_recurse(PlannerInfo *root, Path *best_path);
static Plan *create_scan_plan(PlannerInfo *root, Path *best_path);
-static List *build_relation_tlist(RelOptInfo *rel);
+static List *build_path_tlist(PlannerInfo *root, Path *path);
static bool use_physical_tlist(PlannerInfo *root, RelOptInfo *rel);
-static void disuse_physical_tlist(Plan *plan, Path *path);
+static void disuse_physical_tlist(PlannerInfo *root, Plan *plan, Path *path);
static Plan *create_gating_plan(PlannerInfo *root, Plan *plan, List *quals);
static Plan *create_join_plan(PlannerInfo *root, JoinPath *best_path);
static Plan *create_append_plan(PlannerInfo *root, AppendPath *best_path);
@@ -305,21 +305,12 @@ create_scan_plan(PlannerInfo *root, Path *best_path)
tlist = build_physical_tlist(root, rel);
/* if fail because of dropped cols, use regular method */
if (tlist == NIL)
- tlist = build_relation_tlist(rel);
+ tlist = build_path_tlist(root, best_path);
}
}
else
{
- tlist = build_relation_tlist(rel);
-
- /*
- * If it's a parameterized otherrel, there might be lateral references
- * in the tlist, which need to be replaced with Params. This cannot
- * happen for regular baserels, though. Note use_physical_tlist()
- * always fails for otherrels, so we don't need to check this above.
- */
- if (rel->reloptkind != RELOPT_BASEREL && best_path->param_info)
- tlist = (List *) replace_nestloop_params(root, (Node *) tlist);
+ tlist = build_path_tlist(root, best_path);
}
/*
@@ -439,11 +430,12 @@ create_scan_plan(PlannerInfo *root, Path *best_path)
}
/*
- * Build a target list (ie, a list of TargetEntry) for a relation.
+ * Build a target list (ie, a list of TargetEntry) for the Path's output.
*/
static List *
-build_relation_tlist(RelOptInfo *rel)
+build_path_tlist(PlannerInfo *root, Path *path)
{
+ RelOptInfo *rel = path->parent;
List *tlist = NIL;
int resno = 1;
ListCell *v;
@@ -453,6 +445,15 @@ build_relation_tlist(RelOptInfo *rel)
/* Do we really need to copy here? Not sure */
Node *node = (Node *) copyObject(lfirst(v));
+ /*
+ * If it's a parameterized path, there might be lateral references in
+ * the tlist, which need to be replaced with Params. There's no need
+ * to remake the TargetEntry nodes, so apply this to each list item
+ * separately.
+ */
+ if (path->param_info)
+ node = replace_nestloop_params(root, node);
+
tlist = lappend(tlist, makeTargetEntry((Expr *) node,
resno,
NULL,
@@ -528,7 +529,7 @@ use_physical_tlist(PlannerInfo *root, RelOptInfo *rel)
* and Material nodes want this, so they don't have to store useless columns.
*/
static void
-disuse_physical_tlist(Plan *plan, Path *path)
+disuse_physical_tlist(PlannerInfo *root, Plan *plan, Path *path)
{
/* Only need to undo it for path types handled by create_scan_plan() */
switch (path->pathtype)
@@ -544,7 +545,7 @@ disuse_physical_tlist(Plan *plan, Path *path)
case T_CteScan:
case T_WorkTableScan:
case T_ForeignScan:
- plan->targetlist = build_relation_tlist(path->parent);
+ plan->targetlist = build_path_tlist(root, path);
break;
default:
break;
@@ -678,7 +679,7 @@ static Plan *
create_append_plan(PlannerInfo *root, AppendPath *best_path)
{
Append *plan;
- List *tlist = build_relation_tlist(best_path->path.parent);
+ List *tlist = build_path_tlist(root, &best_path->path);
List *subplans = NIL;
ListCell *subpaths;
@@ -733,7 +734,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path)
{
MergeAppend *node = makeNode(MergeAppend);
Plan *plan = &node->plan;
- List *tlist = build_relation_tlist(best_path->path.parent);
+ List *tlist = build_path_tlist(root, &best_path->path);
List *pathkeys = best_path->path.pathkeys;
List *subplans = NIL;
ListCell *subpaths;
@@ -862,7 +863,7 @@ create_material_plan(PlannerInfo *root, MaterialPath *best_path)
subplan = create_plan_recurse(root, best_path->subpath);
/* We don't want any excess columns in the materialized tuples */
- disuse_physical_tlist(subplan, best_path->subpath);
+ disuse_physical_tlist(root, subplan, best_path->subpath);
plan = make_material(subplan);
@@ -911,7 +912,7 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path)
* should be left as-is if we don't need to add any expressions; but if we
* do have to add expressions, then a projection step will be needed at
* runtime anyway, so we may as well remove unneeded items. Therefore
- * newtlist starts from build_relation_tlist() not just a copy of the
+ * newtlist starts from build_path_tlist() not just a copy of the
* subplan's tlist; and we don't install it into the subplan unless we are
* sorting or stuff has to be added.
*/
@@ -919,7 +920,7 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path)
uniq_exprs = best_path->uniq_exprs;
/* initialize modified subplan tlist as just the "required" vars */
- newtlist = build_relation_tlist(best_path->path.parent);
+ newtlist = build_path_tlist(root, &best_path->path);
nextresno = list_length(newtlist) + 1;
newitems = false;
@@ -1009,7 +1010,7 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path)
* subplan tlist.
*/
plan = (Plan *) make_agg(root,
- build_relation_tlist(best_path->path.parent),
+ build_path_tlist(root, &best_path->path),
NIL,
AGG_HASHED,
NULL,
@@ -2028,7 +2029,7 @@ create_nestloop_plan(PlannerInfo *root,
Plan *inner_plan)
{
NestLoop *join_plan;
- List *tlist = build_relation_tlist(best_path->path.parent);
+ List *tlist = build_path_tlist(root, &best_path->path);
List *joinrestrictclauses = best_path->joinrestrictinfo;
List *joinclauses;
List *otherclauses;
@@ -2118,7 +2119,7 @@ create_mergejoin_plan(PlannerInfo *root,
Plan *outer_plan,
Plan *inner_plan)
{
- List *tlist = build_relation_tlist(best_path->jpath.path.parent);
+ List *tlist = build_path_tlist(root, &best_path->jpath.path);
List *joinclauses;
List *otherclauses;
List *mergeclauses;
@@ -2186,7 +2187,7 @@ create_mergejoin_plan(PlannerInfo *root,
*/
if (best_path->outersortkeys)
{
- disuse_physical_tlist(outer_plan, best_path->jpath.outerjoinpath);
+ disuse_physical_tlist(root, outer_plan, best_path->jpath.outerjoinpath);
outer_plan = (Plan *)
make_sort_from_pathkeys(root,
outer_plan,
@@ -2199,7 +2200,7 @@ create_mergejoin_plan(PlannerInfo *root,
if (best_path->innersortkeys)
{
- disuse_physical_tlist(inner_plan, best_path->jpath.innerjoinpath);
+ disuse_physical_tlist(root, inner_plan, best_path->jpath.innerjoinpath);
inner_plan = (Plan *)
make_sort_from_pathkeys(root,
inner_plan,
@@ -2413,7 +2414,7 @@ create_hashjoin_plan(PlannerInfo *root,
Plan *outer_plan,
Plan *inner_plan)
{
- List *tlist = build_relation_tlist(best_path->jpath.path.parent);
+ List *tlist = build_path_tlist(root, &best_path->jpath.path);
List *joinclauses;
List *otherclauses;
List *hashclauses;
@@ -2470,11 +2471,11 @@ create_hashjoin_plan(PlannerInfo *root,
best_path->jpath.outerjoinpath->parent->relids);
/* We don't want any excess columns in the hashed tuples */
- disuse_physical_tlist(inner_plan, best_path->jpath.innerjoinpath);
+ disuse_physical_tlist(root, inner_plan, best_path->jpath.innerjoinpath);
/* If we expect batching, suppress excess columns in outer tuples too */
if (best_path->num_batches > 1)
- disuse_physical_tlist(outer_plan, best_path->jpath.outerjoinpath);
+ disuse_physical_tlist(root, outer_plan, best_path->jpath.outerjoinpath);
/*
* If there is a single join clause and we can identify the outer variable
@@ -2604,16 +2605,37 @@ replace_nestloop_params_mutator(Node *node, PlannerInfo *root)
Assert(phv->phlevelsup == 0);
/*
- * If not to be replaced, just return the PlaceHolderVar unmodified.
- * We use bms_overlap as a cheap/quick test to see if the PHV might be
- * evaluated in the outer rels, and then grab its PlaceHolderInfo to
- * tell for sure.
+ * Check whether we need to replace the PHV. We use bms_overlap as a
+ * cheap/quick test to see if the PHV might be evaluated in the outer
+ * rels, and then grab its PlaceHolderInfo to tell for sure.
*/
- if (!bms_overlap(phv->phrels, root->curOuterRels))
- return node;
- if (!bms_is_subset(find_placeholder_info(root, phv, false)->ph_eval_at,
- root->curOuterRels))
- return node;
+ if (!bms_overlap(phv->phrels, root->curOuterRels) ||
+ !bms_is_subset(find_placeholder_info(root, phv, false)->ph_eval_at,
+ root->curOuterRels))
+ {
+ /*
+ * We can't replace the whole PHV, but we might still need to
+ * replace Vars or PHVs within its expression, in case it ends up
+ * actually getting evaluated here. (It might get evaluated in
+ * this plan node, or some child node; in the latter case we don't
+ * really need to process the expression here, but we haven't got
+ * enough info to tell if that's the case.) Flat-copy the PHV
+ * node and then recurse on its expression.
+ *
+ * Note that after doing this, we might have different
+ * representations of the contents of the same PHV in different
+ * parts of the plan tree. This is OK because equal() will just
+ * match on phid/phlevelsup, so setrefs.c will still recognize an
+ * upper-level reference to a lower-level copy of the same PHV.
+ */
+ PlaceHolderVar *newphv = makeNode(PlaceHolderVar);
+
+ memcpy(newphv, phv, sizeof(PlaceHolderVar));
+ newphv->phexpr = (Expr *)
+ replace_nestloop_params_mutator((Node *) phv->phexpr,
+ root);
+ return (Node *) newphv;
+ }
/* Create a Param representing the PlaceHolderVar */
param = assign_nestloop_param_placeholdervar(root, phv);
/* Is this param already listed in root->curOuterParams? */