diff options
Diffstat (limited to 'src/backend/optimizer/util/placeholder.c')
-rw-r--r-- | src/backend/optimizer/util/placeholder.c | 113 |
1 files changed, 88 insertions, 25 deletions
diff --git a/src/backend/optimizer/util/placeholder.c b/src/backend/optimizer/util/placeholder.c index da92497689b..5049ba1c5a9 100644 --- a/src/backend/optimizer/util/placeholder.c +++ b/src/backend/optimizer/util/placeholder.c @@ -69,6 +69,7 @@ find_placeholder_info(PlannerInfo *root, PlaceHolderVar *phv, bool create_new_ph) { PlaceHolderInfo *phinfo; + Relids rels_used; ListCell *lc; /* if this ever isn't true, we'd need to be able to look in parent lists */ @@ -89,8 +90,24 @@ find_placeholder_info(PlannerInfo *root, PlaceHolderVar *phv, phinfo->phid = phv->phid; phinfo->ph_var = copyObject(phv); - /* initialize ph_eval_at as the set of contained relids */ - phinfo->ph_eval_at = pull_varnos((Node *) phv); + + /* + * Any referenced rels that are outside the PHV's syntactic scope are + * LATERAL references, which should be included in ph_lateral but not in + * ph_eval_at. If no referenced rels are within the syntactic scope, + * force evaluation at the syntactic location. + */ + rels_used = pull_varnos((Node *) phv->phexpr); + phinfo->ph_lateral = bms_difference(rels_used, phv->phrels); + if (bms_is_empty(phinfo->ph_lateral)) + phinfo->ph_lateral = NULL; /* make it exactly NULL if empty */ + phinfo->ph_eval_at = bms_int_members(rels_used, phv->phrels); + /* If no contained vars, force evaluation at syntactic location */ + if (bms_is_empty(phinfo->ph_eval_at)) + { + phinfo->ph_eval_at = bms_copy(phv->phrels); + Assert(!bms_is_empty(phinfo->ph_eval_at)); + } /* ph_eval_at may change later, see update_placeholder_eval_levels */ phinfo->ph_needed = NULL; /* initially it's unused */ /* for the moment, estimate width using just the datatype info */ @@ -115,6 +132,12 @@ find_placeholder_info(PlannerInfo *root, PlaceHolderVar *phv, * * We don't need to look at the targetlist because build_base_rel_tlists() * will already have made entries for any PHVs in the tlist. + * + * This is called before we begin deconstruct_jointree. Once we begin + * deconstruct_jointree, all active placeholders must be present in + * root->placeholder_list, because make_outerjoininfo and + * update_placeholder_eval_levels require this info to be available + * while we crawl up the join tree. */ void find_placeholders_in_jointree(PlannerInfo *root) @@ -219,7 +242,7 @@ find_placeholders_in_expr(PlannerInfo *root, Node *expr) * The initial eval_at level set by find_placeholder_info was the set of * rels used in the placeholder's expression (or the whole subselect below * the placeholder's syntactic location, if the expr is variable-free). - * If the subselect contains any outer joins that can null any of those rels, + * If the query contains any outer joins that can null any of those rels, * we must delay evaluation to above those joins. * * We repeat this operation each time we add another outer join to @@ -299,6 +322,9 @@ update_placeholder_eval_levels(PlannerInfo *root, SpecialJoinInfo *new_sjinfo) } } while (found_some); + /* Can't move the PHV's eval_at level to above its syntactic level */ + Assert(bms_is_subset(eval_at, syn_level)); + phinfo->ph_eval_at = eval_at; } } @@ -309,11 +335,14 @@ update_placeholder_eval_levels(PlannerInfo *root, SpecialJoinInfo *new_sjinfo) * * This is called after we've finished determining the eval_at levels for * all placeholders. We need to make sure that all vars and placeholders - * needed to evaluate each placeholder will be available at the join level - * where the evaluation will be done. Note that this loop can have - * side-effects on the ph_needed sets of other PlaceHolderInfos; that's okay - * because we don't examine ph_needed here, so there are no ordering issues - * to worry about. + * needed to evaluate each placeholder will be available at the scan or join + * level where the evaluation will be done. (It might seem that scan-level + * evaluations aren't interesting, but that's not so: a LATERAL reference + * within a placeholder's expression needs to cause the referenced var or + * placeholder to be marked as needed in the scan where it's evaluated.) + * Note that this loop can have side-effects on the ph_needed sets of other + * PlaceHolderInfos; that's okay because we don't examine ph_needed here, so + * there are no ordering issues to worry about. */ void fix_placeholder_input_needed_levels(PlannerInfo *root) @@ -323,27 +352,23 @@ fix_placeholder_input_needed_levels(PlannerInfo *root) foreach(lc, root->placeholder_list) { PlaceHolderInfo *phinfo = (PlaceHolderInfo *) lfirst(lc); - Relids eval_at = phinfo->ph_eval_at; - - /* No work unless it'll be evaluated above baserel level */ - if (bms_membership(eval_at) == BMS_MULTIPLE) - { - List *vars = pull_var_clause((Node *) phinfo->ph_var->phexpr, - PVC_RECURSE_AGGREGATES, - PVC_INCLUDE_PLACEHOLDERS); + List *vars = pull_var_clause((Node *) phinfo->ph_var->phexpr, + PVC_RECURSE_AGGREGATES, + PVC_INCLUDE_PLACEHOLDERS); - add_vars_to_targetlist(root, vars, eval_at, false); - list_free(vars); - } + add_vars_to_targetlist(root, vars, phinfo->ph_eval_at, false); + list_free(vars); } } /* * add_placeholders_to_base_rels - * Add any required PlaceHolderVars to base rels' targetlists. + * Add any required PlaceHolderVars to base rels' targetlists, and + * update lateral_vars lists for lateral references contained in them. * * If any placeholder can be computed at a base rel and is needed above it, - * add it to that rel's targetlist. This might look like it could be merged + * add it to that rel's targetlist, and add any lateral references it requires + * to the rel's lateral_vars list. This might look like it could be merged * with fix_placeholder_input_needed_levels, but it must be separate because * join removal happens in between, and can change the ph_eval_at sets. There * is essentially the same logic in add_placeholders_to_joinrel, but we can't @@ -359,14 +384,52 @@ add_placeholders_to_base_rels(PlannerInfo *root) PlaceHolderInfo *phinfo = (PlaceHolderInfo *) lfirst(lc); Relids eval_at = phinfo->ph_eval_at; - if (bms_membership(eval_at) == BMS_SINGLETON && - bms_nonempty_difference(phinfo->ph_needed, eval_at)) + if (bms_membership(eval_at) == BMS_SINGLETON) { int varno = bms_singleton_member(eval_at); RelOptInfo *rel = find_base_rel(root, varno); - rel->reltargetlist = lappend(rel->reltargetlist, - copyObject(phinfo->ph_var)); + /* add it to reltargetlist if needed above the rel scan level */ + if (bms_nonempty_difference(phinfo->ph_needed, eval_at)) + rel->reltargetlist = lappend(rel->reltargetlist, + copyObject(phinfo->ph_var)); + /* if there are lateral refs in it, add them to lateral_vars */ + 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 (var->varno != varno) + rel->lateral_vars = lappend(rel->lateral_vars, + var); + } + 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)) + rel->lateral_vars = lappend(rel->lateral_vars, + other_phv); + } + else + Assert(false); + } + + list_free(vars); + } } } } |