diff options
Diffstat (limited to 'src/backend/optimizer/plan')
-rw-r--r-- | src/backend/optimizer/plan/analyzejoins.c | 28 | ||||
-rw-r--r-- | src/backend/optimizer/plan/createplan.c | 100 | ||||
-rw-r--r-- | src/backend/optimizer/plan/initsplan.c | 187 | ||||
-rw-r--r-- | src/backend/optimizer/plan/planmain.c | 13 |
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. |