aboutsummaryrefslogtreecommitdiff
path: root/src/backend/optimizer/plan
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/optimizer/plan')
-rw-r--r--src/backend/optimizer/plan/analyzejoins.c28
-rw-r--r--src/backend/optimizer/plan/createplan.c100
-rw-r--r--src/backend/optimizer/plan/initsplan.c187
-rw-r--r--src/backend/optimizer/plan/planmain.c13
4 files changed, 230 insertions, 98 deletions
diff --git a/src/backend/optimizer/plan/analyzejoins.c b/src/backend/optimizer/plan/analyzejoins.c
index daab355b1d3..2271a7c35e0 100644
--- a/src/backend/optimizer/plan/analyzejoins.c
+++ b/src/backend/optimizer/plan/analyzejoins.c
@@ -202,7 +202,9 @@ join_is_removable(PlannerInfo *root, SpecialJoinInfo *sjinfo)
* that will be used above the join. We only need to fail if such a PHV
* actually references some inner-rel attributes; but the correct check
* for that is relatively expensive, so we first check against ph_eval_at,
- * which must mention the inner rel if the PHV uses any inner-rel attrs.
+ * which must mention the inner rel if the PHV uses any inner-rel attrs as
+ * non-lateral references. Note that if the PHV's syntactic scope is just
+ * the inner rel, we can't drop the rel even if the PHV is variable-free.
*/
foreach(l, root->placeholder_list)
{
@@ -210,9 +212,13 @@ join_is_removable(PlannerInfo *root, SpecialJoinInfo *sjinfo)
if (bms_is_subset(phinfo->ph_needed, joinrelids))
continue; /* PHV is not used above the join */
+ if (bms_overlap(phinfo->ph_lateral, innerrel->relids))
+ return false; /* it references innerrel laterally */
if (!bms_overlap(phinfo->ph_eval_at, innerrel->relids))
continue; /* it definitely doesn't reference innerrel */
- if (bms_overlap(pull_varnos((Node *) phinfo->ph_var),
+ if (bms_is_subset(phinfo->ph_eval_at, innerrel->relids))
+ return false; /* there isn't any other place to eval PHV */
+ if (bms_overlap(pull_varnos((Node *) phinfo->ph_var->phexpr),
innerrel->relids))
return false; /* it does reference innerrel */
}
@@ -355,7 +361,7 @@ remove_rel_from_query(PlannerInfo *root, int relid, Relids joinrelids)
* Likewise remove references from LateralJoinInfo data structures.
*
* If we are deleting a LATERAL subquery, we can forget its
- * LateralJoinInfo altogether. Otherwise, make sure the target is not
+ * LateralJoinInfos altogether. Otherwise, make sure the target is not
* included in any lateral_lhs set. (It probably can't be, since that
* should have precluded deciding to remove it; but let's cope anyway.)
*/
@@ -364,29 +370,27 @@ remove_rel_from_query(PlannerInfo *root, int relid, Relids joinrelids)
LateralJoinInfo *ljinfo = (LateralJoinInfo *) lfirst(l);
nextl = lnext(l);
- if (ljinfo->lateral_rhs == relid)
+ ljinfo->lateral_rhs = bms_del_member(ljinfo->lateral_rhs, relid);
+ if (bms_is_empty(ljinfo->lateral_rhs))
root->lateral_info_list = list_delete_ptr(root->lateral_info_list,
ljinfo);
else
+ {
ljinfo->lateral_lhs = bms_del_member(ljinfo->lateral_lhs, relid);
+ Assert(!bms_is_empty(ljinfo->lateral_lhs));
+ }
}
/*
* Likewise remove references from PlaceHolderVar data structures.
- *
- * Here we have a special case: if a PHV's eval_at set is just the target
- * relid, we want to leave it that way instead of reducing it to the empty
- * set. An empty eval_at set would confuse later processing since it
- * would match every possible eval placement.
*/
foreach(l, root->placeholder_list)
{
PlaceHolderInfo *phinfo = (PlaceHolderInfo *) lfirst(l);
phinfo->ph_eval_at = bms_del_member(phinfo->ph_eval_at, relid);
- if (bms_is_empty(phinfo->ph_eval_at)) /* oops, belay that */
- phinfo->ph_eval_at = bms_add_member(phinfo->ph_eval_at, relid);
-
+ Assert(!bms_is_empty(phinfo->ph_eval_at));
+ Assert(!bms_is_member(relid, phinfo->ph_lateral));
phinfo->ph_needed = bms_del_member(phinfo->ph_needed, relid);
}
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? */
diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c
index 07c4dddd24e..98f601cdede 100644
--- a/src/backend/optimizer/plan/initsplan.c
+++ b/src/backend/optimizer/plan/initsplan.c
@@ -38,7 +38,7 @@ int join_collapse_limit;
static void extract_lateral_references(PlannerInfo *root, RelOptInfo *brel,
Index rtindex);
-static void add_lateral_info(PlannerInfo *root, Index rhs, Relids lhs);
+static void add_lateral_info(PlannerInfo *root, Relids lhs, Relids rhs);
static List *deconstruct_recurse(PlannerInfo *root, Node *jtnode,
bool below_outer_join,
Relids *qualscope, Relids *inner_join_rels);
@@ -177,6 +177,8 @@ add_vars_to_targetlist(PlannerInfo *root, List *vars,
RelOptInfo *rel = find_base_rel(root, var->varno);
int attno = var->varattno;
+ if (bms_is_subset(where_needed, rel->relids))
+ continue;
Assert(attno >= rel->min_attr && attno <= rel->max_attr);
attno -= rel->min_attr;
if (rel->attr_needed[attno] == NULL)
@@ -221,6 +223,12 @@ add_vars_to_targetlist(PlannerInfo *root, List *vars,
* ensure that the Vars/PHVs propagate up to the nestloop join level; this
* means setting suitable where_needed values for them.
*
+ * Note that this only deals with lateral references in unflattened LATERAL
+ * subqueries. When we flatten a LATERAL subquery, its lateral references
+ * become plain Vars in the parent query, but they may have to be wrapped in
+ * PlaceHolderVars if they need to be forced NULL by outer joins that don't
+ * also null the LATERAL subquery. That's all handled elsewhere.
+ *
* This has to run before deconstruct_jointree, since it might result in
* creation of PlaceHolderInfos.
*/
@@ -250,16 +258,18 @@ find_lateral_references(PlannerInfo *root)
* This bit is less obvious than it might look. We ignore appendrel
* otherrels and consider only their parent baserels. In a case where
* a LATERAL-containing UNION ALL subquery was pulled up, it is the
- * otherrels that are actually going to be in the plan. However, we
- * want to mark all their lateral references as needed by the parent,
+ * otherrel that is actually going to be in the plan. However, we
+ * want to mark all its lateral references as needed by the parent,
* because it is the parent's relid that will be used for join
* planning purposes. And the parent's RTE will contain all the
- * lateral references we need to know, since the pulled-up members are
- * nothing but copies of parts of the original RTE's subquery. We
- * could visit the children instead and transform their references
- * back to the parent's relid, but it would be much more complicated
- * for no real gain. (Important here is that the child members have
- * not yet received any processing beyond being pulled up.)
+ * lateral references we need to know, since the pulled-up member is
+ * nothing but a copy of parts of the original RTE's subquery. We
+ * could visit the parent's children instead and transform their
+ * references back to the parent's relid, but it would be much more
+ * complicated for no real gain. (Important here is that the child
+ * members have not yet received any processing beyond being pulled
+ * up.) Similarly, in appendrels created by inheritance expansion,
+ * it's sufficient to look at the parent relation.
*/
/* ignore RTEs that are "other rels" */
@@ -346,7 +356,10 @@ extract_lateral_references(PlannerInfo *root, RelOptInfo *brel, Index rtindex)
*/
where_needed = bms_make_singleton(rtindex);
- /* Push the Vars into their source relations' targetlists */
+ /*
+ * Push Vars into their source relations' targetlists, and PHVs into
+ * root->placeholder_list.
+ */
add_vars_to_targetlist(root, newvars, where_needed, true);
/* Remember the lateral references for create_lateral_join_info */
@@ -355,16 +368,20 @@ extract_lateral_references(PlannerInfo *root, RelOptInfo *brel, Index rtindex)
/*
* create_lateral_join_info
- * For each LATERAL subquery, create LateralJoinInfo(s) and add them to
- * root->lateral_info_list, and fill in the per-rel lateral_relids sets.
+ * For each unflattened LATERAL subquery, create LateralJoinInfo(s) and add
+ * them to root->lateral_info_list, and fill in the per-rel lateral_relids
+ * and lateral_referencers sets. Also generate LateralJoinInfo(s) to
+ * represent any lateral references within PlaceHolderVars (this part deals
+ * with the effects of flattened LATERAL subqueries).
*
* This has to run after deconstruct_jointree, because we need to know the
- * final ph_eval_at values for referenced PlaceHolderVars.
+ * final ph_eval_at values for PlaceHolderVars.
*/
void
create_lateral_join_info(PlannerInfo *root)
{
Index rti;
+ ListCell *lc;
/* We need do nothing if the query contains no LATERAL RTEs */
if (!root->hasLateralRTEs)
@@ -377,7 +394,6 @@ create_lateral_join_info(PlannerInfo *root)
{
RelOptInfo *brel = root->simple_rel_array[rti];
Relids lateral_relids;
- ListCell *lc;
/* there may be empty slots corresponding to non-baserel RTEs */
if (brel == NULL)
@@ -400,7 +416,8 @@ create_lateral_join_info(PlannerInfo *root)
{
Var *var = (Var *) node;
- add_lateral_info(root, rti, bms_make_singleton(var->varno));
+ add_lateral_info(root, bms_make_singleton(var->varno),
+ brel->relids);
lateral_relids = bms_add_member(lateral_relids,
var->varno);
}
@@ -410,7 +427,7 @@ create_lateral_join_info(PlannerInfo *root)
PlaceHolderInfo *phinfo = find_placeholder_info(root, phv,
false);
- add_lateral_info(root, rti, bms_copy(phinfo->ph_eval_at));
+ add_lateral_info(root, phinfo->ph_eval_at, brel->relids);
lateral_relids = bms_add_members(lateral_relids,
phinfo->ph_eval_at);
}
@@ -422,15 +439,100 @@ create_lateral_join_info(PlannerInfo *root)
if (bms_is_empty(lateral_relids))
continue; /* ensure lateral_relids is NULL if empty */
brel->lateral_relids = lateral_relids;
+ }
+
+ /*
+ * Now check for lateral references within PlaceHolderVars, and make
+ * LateralJoinInfos describing each such reference. Unlike references in
+ * unflattened LATERAL RTEs, the referencing location could be a join.
+ */
+ foreach(lc, root->placeholder_list)
+ {
+ PlaceHolderInfo *phinfo = (PlaceHolderInfo *) lfirst(lc);
+ Relids eval_at = phinfo->ph_eval_at;
+
+ if (phinfo->ph_lateral != NULL)
+ {
+ List *vars = pull_var_clause((Node *) phinfo->ph_var->phexpr,
+ PVC_RECURSE_AGGREGATES,
+ PVC_INCLUDE_PLACEHOLDERS);
+ ListCell *lc2;
+
+ foreach(lc2, vars)
+ {
+ Node *node = (Node *) lfirst(lc2);
+
+ if (IsA(node, Var))
+ {
+ Var *var = (Var *) node;
+
+ if (!bms_is_member(var->varno, eval_at))
+ add_lateral_info(root,
+ bms_make_singleton(var->varno),
+ eval_at);
+ }
+ else if (IsA(node, PlaceHolderVar))
+ {
+ PlaceHolderVar *other_phv = (PlaceHolderVar *) node;
+ PlaceHolderInfo *other_phi;
+
+ other_phi = find_placeholder_info(root, other_phv,
+ false);
+ if (!bms_is_subset(other_phi->ph_eval_at, eval_at))
+ add_lateral_info(root, other_phi->ph_eval_at, eval_at);
+ }
+ else
+ Assert(false);
+ }
+
+ list_free(vars);
+ }
+ }
+
+ /* If we found no lateral references, we're done. */
+ if (root->lateral_info_list == NIL)
+ return;
+
+ /*
+ * Now that we've identified all lateral references, make a second pass in
+ * which we mark each baserel with the set of relids of rels that
+ * reference it laterally (essentially, the inverse mapping of
+ * lateral_relids). We'll need this for join_clause_is_movable_to().
+ *
+ * Also, propagate lateral_relids and lateral_referencers from appendrel
+ * parent rels to their child rels. We intentionally give each child rel
+ * the same minimum parameterization, even though it's quite possible that
+ * some don't reference all the lateral rels. This is because any append
+ * path for the parent will have to have the same parameterization for
+ * every child anyway, and there's no value in forcing extra
+ * reparameterize_path() calls. Similarly, a lateral reference to the
+ * parent prevents use of otherwise-movable join rels for each child.
+ */
+ for (rti = 1; rti < root->simple_rel_array_size; rti++)
+ {
+ RelOptInfo *brel = root->simple_rel_array[rti];
+ Relids lateral_referencers;
+
+ if (brel == NULL)
+ continue;
+ if (brel->reloptkind != RELOPT_BASEREL)
+ continue;
+
+ /* Compute lateral_referencers using the finished lateral_info_list */
+ lateral_referencers = NULL;
+ foreach(lc, root->lateral_info_list)
+ {
+ LateralJoinInfo *ljinfo = (LateralJoinInfo *) lfirst(lc);
+
+ if (bms_is_member(brel->relid, ljinfo->lateral_lhs))
+ lateral_referencers = bms_add_members(lateral_referencers,
+ ljinfo->lateral_rhs);
+ }
+ brel->lateral_referencers = lateral_referencers;
/*
- * If it's an appendrel parent, copy its lateral_relids to each child
- * rel. We intentionally give each child rel the same minimum
- * parameterization, even though it's quite possible that some don't
- * reference all the lateral rels. This is because any append path
- * for the parent will have to have the same parameterization for
- * every child anyway, and there's no value in forcing extra
- * reparameterize_path() calls.
+ * If it's an appendrel parent, copy its lateral_relids and
+ * lateral_referencers to each child rel.
*/
if (root->simple_rte_array[rti]->inh)
{
@@ -444,7 +546,9 @@ create_lateral_join_info(PlannerInfo *root)
childrel = root->simple_rel_array[appinfo->child_relid];
Assert(childrel->reloptkind == RELOPT_OTHER_MEMBER_REL);
Assert(childrel->lateral_relids == NULL);
- childrel->lateral_relids = lateral_relids;
+ childrel->lateral_relids = brel->lateral_relids;
+ Assert(childrel->lateral_referencers == NULL);
+ childrel->lateral_referencers = brel->lateral_referencers;
}
}
}
@@ -454,38 +558,39 @@ create_lateral_join_info(PlannerInfo *root)
* add_lateral_info
* Add a LateralJoinInfo to root->lateral_info_list, if needed
*
- * We suppress redundant list entries. The passed lhs set must be freshly
- * made; we free it if not used in a new list entry.
+ * We suppress redundant list entries. The passed Relids are copied if saved.
*/
static void
-add_lateral_info(PlannerInfo *root, Index rhs, Relids lhs)
+add_lateral_info(PlannerInfo *root, Relids lhs, Relids rhs)
{
LateralJoinInfo *ljinfo;
- ListCell *l;
+ ListCell *lc;
- Assert(!bms_is_member(rhs, lhs));
+ /* Sanity-check the input */
+ Assert(!bms_is_empty(lhs));
+ Assert(!bms_is_empty(rhs));
+ Assert(!bms_overlap(lhs, rhs));
/*
- * If an existing list member has the same RHS and an LHS that is a subset
- * of the new one, it's redundant, but we don't trouble to get rid of it.
- * The only case that is really worth worrying about is identical entries,
- * and we handle that well enough with this simple logic.
+ * The input is redundant if it has the same RHS and an LHS that is a
+ * subset of an existing entry's. If an existing entry has the same RHS
+ * and an LHS that is a subset of the new one, it's redundant, but we
+ * don't trouble to get rid of it. The only case that is really worth
+ * worrying about is identical entries, and we handle that well enough
+ * with this simple logic.
*/
- foreach(l, root->lateral_info_list)
+ foreach(lc, root->lateral_info_list)
{
- ljinfo = (LateralJoinInfo *) lfirst(l);
- if (rhs == ljinfo->lateral_rhs &&
+ ljinfo = (LateralJoinInfo *) lfirst(lc);
+ if (bms_equal(rhs, ljinfo->lateral_rhs) &&
bms_is_subset(lhs, ljinfo->lateral_lhs))
- {
- bms_free(lhs);
return;
- }
}
/* Not there, so make a new entry */
ljinfo = makeNode(LateralJoinInfo);
- ljinfo->lateral_rhs = rhs;
- ljinfo->lateral_lhs = lhs;
+ ljinfo->lateral_lhs = bms_copy(lhs);
+ ljinfo->lateral_rhs = bms_copy(rhs);
root->lateral_info_list = lappend(root->lateral_info_list, ljinfo);
}
diff --git a/src/backend/optimizer/plan/planmain.c b/src/backend/optimizer/plan/planmain.c
index 42a98945a38..284929f125e 100644
--- a/src/backend/optimizer/plan/planmain.c
+++ b/src/backend/optimizer/plan/planmain.c
@@ -176,12 +176,6 @@ query_planner(PlannerInfo *root, List *tlist,
joinlist = deconstruct_jointree(root);
/*
- * Create the LateralJoinInfo list now that we have finalized
- * PlaceHolderVar eval levels.
- */
- create_lateral_join_info(root);
-
- /*
* Reconsider any postponed outer-join quals now that we have built up
* equivalence classes. (This could result in further additions or
* mergings of classes.)
@@ -226,6 +220,13 @@ query_planner(PlannerInfo *root, List *tlist,
add_placeholders_to_base_rels(root);
/*
+ * Create the LateralJoinInfo list now that we have finalized
+ * PlaceHolderVar eval levels and made any necessary additions to the
+ * lateral_vars lists for lateral references within PlaceHolderVars.
+ */
+ create_lateral_join_info(root);
+
+ /*
* We should now have size estimates for every actual table involved in
* the query, and we also know which if any have been deleted from the
* query by join removal; so we can compute total_table_pages.