diff options
Diffstat (limited to 'src/backend/optimizer/plan/initsplan.c')
-rw-r--r-- | src/backend/optimizer/plan/initsplan.c | 267 |
1 files changed, 240 insertions, 27 deletions
diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c index 81f7cf2e9d8..824c6a7e473 100644 --- a/src/backend/optimizer/plan/initsplan.c +++ b/src/backend/optimizer/plan/initsplan.c @@ -22,9 +22,11 @@ #include "optimizer/paths.h" #include "optimizer/placeholder.h" #include "optimizer/planmain.h" +#include "optimizer/planner.h" #include "optimizer/prep.h" #include "optimizer/restrictinfo.h" #include "optimizer/var.h" +#include "rewrite/rewriteManip.h" #include "utils/lsyscache.h" @@ -33,6 +35,9 @@ int from_collapse_limit; 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 List *deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join, Relids *qualscope, Relids *inner_join_rels); @@ -204,18 +209,75 @@ add_vars_to_targetlist(PlannerInfo *root, List *vars, } } + +/***************************************************************************** + * + * LATERAL REFERENCES + * + *****************************************************************************/ + /* - * extract_lateral_references - * If the specified RTE is a LATERAL subquery, extract all its references - * to Vars of the current query level, and make sure those Vars will be - * available for evaluation of the RTE. + * find_lateral_references + * For each LATERAL subquery, extract all its references to Vars and + * PlaceHolderVars of the current query level, and make sure those values + * will be available for evaluation of the subquery. * - * XXX this is rather duplicative of processing that has to happen elsewhere. - * Maybe it'd be a good idea to do this type of extraction further upstream - * and save the results? + * While later planning steps ensure that the Var/PHV source rels are on the + * outside of nestloops relative to the LATERAL subquery, we also need to + * ensure that the Vars/PHVs propagate up to the nestloop join level; this + * means setting suitable where_needed values for them. + * + * This has to run before deconstruct_jointree, since it might result in + * creation of PlaceHolderInfos or extension of their ph_may_need sets. */ +void +find_lateral_references(PlannerInfo *root) +{ + Index rti; + + /* We need do nothing if the query contains no LATERAL RTEs */ + if (!root->hasLateralRTEs) + return; + + /* + * Examine all baserels (the rel array has been set up by now). + */ + for (rti = 1; rti < root->simple_rel_array_size; rti++) + { + RelOptInfo *brel = root->simple_rel_array[rti]; + + /* there may be empty slots corresponding to non-baserel RTEs */ + if (brel == NULL) + continue; + + Assert(brel->relid == rti); /* sanity check on array */ + + /* + * 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, + * 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.) + */ + + /* ignore RTEs that are "other rels" */ + if (brel->reloptkind != RELOPT_BASEREL) + continue; + + extract_lateral_references(root, brel, rti); + } +} + static void -extract_lateral_references(PlannerInfo *root, int rtindex) +extract_lateral_references(PlannerInfo *root, RelOptInfo *brel, Index rtindex) { RangeTblEntry *rte = root->simple_rte_array[rtindex]; List *vars; @@ -235,35 +297,52 @@ extract_lateral_references(PlannerInfo *root, int rtindex) else if (rte->rtekind == RTE_VALUES) vars = pull_vars_of_level((Node *) rte->values_lists, 0); else - return; + { + Assert(false); + return; /* keep compiler quiet */ + } + + if (vars == NIL) + return; /* nothing to do */ /* Copy each Var (or PlaceHolderVar) and adjust it to match our level */ newvars = NIL; foreach(lc, vars) { - Node *var = (Node *) lfirst(lc); + Node *node = (Node *) lfirst(lc); - var = copyObject(var); - if (IsA(var, Var)) + node = copyObject(node); + if (IsA(node, Var)) { - ((Var *) var)->varlevelsup = 0; + Var *var = (Var *) node; + + /* Adjustment is easy since it's just one node */ + var->varlevelsup = 0; } - else if (IsA(var, PlaceHolderVar)) + else if (IsA(node, PlaceHolderVar)) { + PlaceHolderVar *phv = (PlaceHolderVar *) node; + int levelsup = phv->phlevelsup; + + /* Have to work harder to adjust the contained expression too */ + if (levelsup != 0) + IncrementVarSublevelsUp(node, -levelsup, 0); + /* - * It's sufficient to set phlevelsup = 0, because we call - * add_vars_to_targetlist with create_new_ph = false (as we must, - * because deconstruct_jointree has already started); therefore - * nobody is going to look at the contained expression to notice - * whether its Vars have the right level. + * If we pulled the PHV out of a subquery RTE, its expression + * needs to be preprocessed. subquery_planner() already did this + * for level-zero PHVs in function and values RTEs, though. */ - ((PlaceHolderVar *) var)->phlevelsup = 0; + if (levelsup > 0) + phv->phexpr = preprocess_phv_expression(root, phv->phexpr); } else Assert(false); - newvars = lappend(newvars, var); + newvars = lappend(newvars, node); } + list_free(vars); + /* * We mark the Vars as being "needed" at the LATERAL RTE. This is a bit * of a cheat: a more formal approach would be to mark each one as needed @@ -274,10 +353,146 @@ extract_lateral_references(PlannerInfo *root, int rtindex) where_needed = bms_make_singleton(rtindex); /* Push the Vars into their source relations' targetlists */ - add_vars_to_targetlist(root, newvars, where_needed, false); + add_vars_to_targetlist(root, newvars, where_needed, true); - list_free(newvars); - list_free(vars); + /* Remember the lateral references for create_lateral_join_info */ + brel->lateral_vars = newvars; +} + +/* + * 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. + * + * This has to run after deconstruct_jointree, because we need to know the + * final ph_eval_at values for referenced PlaceHolderVars. + */ +void +create_lateral_join_info(PlannerInfo *root) +{ + Index rti; + + /* We need do nothing if the query contains no LATERAL RTEs */ + if (!root->hasLateralRTEs) + return; + + /* + * Examine all baserels (the rel array has been set up by now). + */ + for (rti = 1; rti < root->simple_rel_array_size; rti++) + { + 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) + continue; + + Assert(brel->relid == rti); /* sanity check on array */ + + /* ignore RTEs that are "other rels" */ + if (brel->reloptkind != RELOPT_BASEREL) + continue; + + lateral_relids = NULL; + + /* consider each laterally-referenced Var or PHV */ + foreach(lc, brel->lateral_vars) + { + Node *node = (Node *) lfirst(lc); + + if (IsA(node, Var)) + { + Var *var = (Var *) node; + + add_lateral_info(root, rti, bms_make_singleton(var->varno)); + lateral_relids = bms_add_member(lateral_relids, + var->varno); + } + else if (IsA(node, PlaceHolderVar)) + { + PlaceHolderVar *phv = (PlaceHolderVar *) node; + PlaceHolderInfo *phinfo = find_placeholder_info(root, phv, + false); + + add_lateral_info(root, rti, bms_copy(phinfo->ph_eval_at)); + lateral_relids = bms_add_members(lateral_relids, + phinfo->ph_eval_at); + } + else + Assert(false); + } + + /* We now know all the relids needed for lateral refs in this rel */ + if (bms_is_empty(lateral_relids)) + continue; /* ensure lateral_relids is NULL if empty */ + brel->lateral_relids = lateral_relids; + + /* + * 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 (root->simple_rte_array[rti]->inh) + { + foreach(lc, root->append_rel_list) + { + AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(lc); + RelOptInfo *childrel; + + if (appinfo->parent_relid != rti) + continue; + 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; + } + } + } +} + +/* + * 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. + */ +static void +add_lateral_info(PlannerInfo *root, Index rhs, Relids lhs) +{ + LateralJoinInfo *ljinfo; + ListCell *l; + + Assert(!bms_is_member(rhs, lhs)); + + /* + * 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. + */ + foreach(l, root->lateral_info_list) + { + ljinfo = (LateralJoinInfo *) lfirst(l); + if (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; + root->lateral_info_list = lappend(root->lateral_info_list, ljinfo); } @@ -362,9 +577,7 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join, { int varno = ((RangeTblRef *) jtnode)->rtindex; - /* No quals to deal with, but do check for LATERAL subqueries */ - extract_lateral_references(root, varno); - /* Result qualscope is just the one Relid */ + /* No quals to deal with, just return correct result */ *qualscope = bms_make_singleton(varno); /* A single baserel does not create an inner join */ *inner_join_rels = NULL; |