| /*------------------------------------------------------------------------- |
| * |
| * placeholder.c |
| * PlaceHolderVar and PlaceHolderInfo manipulation routines |
| * |
| * |
| * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group |
| * Portions Copyright (c) 1994, Regents of the University of California |
| * |
| * |
| * IDENTIFICATION |
| * src/backend/optimizer/util/placeholder.c |
| * |
| *------------------------------------------------------------------------- |
| */ |
| #include "postgres.h" |
| |
| #include "nodes/nodeFuncs.h" |
| #include "optimizer/cost.h" |
| #include "optimizer/optimizer.h" |
| #include "optimizer/pathnode.h" |
| #include "optimizer/placeholder.h" |
| #include "optimizer/planmain.h" |
| #include "utils/lsyscache.h" |
| |
| /* Local functions */ |
| static void find_placeholders_recurse(PlannerInfo *root, Node *jtnode); |
| static void find_placeholders_in_expr(PlannerInfo *root, Node *expr); |
| |
| |
| /* |
| * make_placeholder_expr |
| * Make a PlaceHolderVar for the given expression. |
| * |
| * phrels is the syntactic location (as a set of baserels) to attribute |
| * to the expression. |
| */ |
| PlaceHolderVar * |
| make_placeholder_expr(PlannerInfo *root, Expr *expr, Relids phrels) |
| { |
| PlaceHolderVar *phv = makeNode(PlaceHolderVar); |
| |
| phv->phexpr = expr; |
| phv->phrels = phrels; |
| phv->phid = ++(root->glob->lastPHId); |
| phv->phlevelsup = 0; |
| |
| return phv; |
| } |
| |
| /* |
| * find_placeholder_info |
| * Fetch the PlaceHolderInfo for the given PHV |
| * |
| * If the PlaceHolderInfo doesn't exist yet, create it if create_new_ph is |
| * true, else throw an error. |
| * |
| * This is separate from make_placeholder_expr because subquery pullup has |
| * to make PlaceHolderVars for expressions that might not be used at all in |
| * the upper query, or might not remain after const-expression simplification. |
| * We build PlaceHolderInfos only for PHVs that are still present in the |
| * simplified query passed to query_planner(). |
| * |
| * Note: this should only be called after query_planner() has started. Also, |
| * create_new_ph must not be true after deconstruct_jointree begins, because |
| * make_outerjoininfo assumes that we already know about all placeholders. |
| */ |
| PlaceHolderInfo * |
| 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 */ |
| Assert(phv->phlevelsup == 0); |
| |
| foreach(lc, root->placeholder_list) |
| { |
| phinfo = (PlaceHolderInfo *) lfirst(lc); |
| if (phinfo->phid == phv->phid) |
| return phinfo; |
| } |
| |
| /* Not found, so create it */ |
| if (!create_new_ph) |
| elog(ERROR, "too late to create a new PlaceHolderInfo"); |
| |
| phinfo = makeNode(PlaceHolderInfo); |
| |
| phinfo->phid = phv->phid; |
| phinfo->ph_var = copyObject(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(root, (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 */ |
| phinfo->ph_width = get_typavgwidth(exprType((Node *) phv->phexpr), |
| exprTypmod((Node *) phv->phexpr)); |
| |
| root->placeholder_list = lappend(root->placeholder_list, phinfo); |
| |
| /* |
| * The PHV's contained expression may contain other, lower-level PHVs. We |
| * now know we need to get those into the PlaceHolderInfo list, too, so we |
| * may as well do that immediately. |
| */ |
| find_placeholders_in_expr(root, (Node *) phinfo->ph_var->phexpr); |
| |
| return phinfo; |
| } |
| |
| /* |
| * find_placeholders_in_jointree |
| * Search the jointree for PlaceHolderVars, and build PlaceHolderInfos |
| * |
| * 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) |
| { |
| /* We need do nothing if the query contains no PlaceHolderVars */ |
| if (root->glob->lastPHId != 0) |
| { |
| /* Start recursion at top of jointree */ |
| Assert(root->parse->jointree != NULL && |
| IsA(root->parse->jointree, FromExpr)); |
| find_placeholders_recurse(root, (Node *) root->parse->jointree); |
| } |
| } |
| |
| /* |
| * find_placeholders_recurse |
| * One recursion level of find_placeholders_in_jointree. |
| * |
| * jtnode is the current jointree node to examine. |
| */ |
| static void |
| find_placeholders_recurse(PlannerInfo *root, Node *jtnode) |
| { |
| if (jtnode == NULL) |
| return; |
| if (IsA(jtnode, RangeTblRef)) |
| { |
| /* No quals to deal with here */ |
| } |
| else if (IsA(jtnode, FromExpr)) |
| { |
| FromExpr *f = (FromExpr *) jtnode; |
| ListCell *l; |
| |
| /* |
| * First, recurse to handle child joins. |
| */ |
| foreach(l, f->fromlist) |
| { |
| find_placeholders_recurse(root, lfirst(l)); |
| } |
| |
| /* |
| * Now process the top-level quals. |
| */ |
| find_placeholders_in_expr(root, f->quals); |
| } |
| else if (IsA(jtnode, JoinExpr)) |
| { |
| JoinExpr *j = (JoinExpr *) jtnode; |
| |
| /* |
| * First, recurse to handle child joins. |
| */ |
| find_placeholders_recurse(root, j->larg); |
| find_placeholders_recurse(root, j->rarg); |
| |
| /* Process the qual clauses */ |
| find_placeholders_in_expr(root, j->quals); |
| } |
| else |
| elog(ERROR, "unrecognized node type: %d", |
| (int) nodeTag(jtnode)); |
| } |
| |
| /* |
| * find_placeholders_in_expr |
| * Find all PlaceHolderVars in the given expression, and create |
| * PlaceHolderInfo entries for them. |
| */ |
| static void |
| find_placeholders_in_expr(PlannerInfo *root, Node *expr) |
| { |
| List *vars; |
| ListCell *vl; |
| |
| /* |
| * pull_var_clause does more than we need here, but it'll do and it's |
| * convenient to use. |
| */ |
| vars = pull_var_clause(expr, |
| PVC_RECURSE_AGGREGATES | |
| PVC_RECURSE_WINDOWFUNCS | |
| PVC_INCLUDE_PLACEHOLDERS); |
| foreach(vl, vars) |
| { |
| PlaceHolderVar *phv = (PlaceHolderVar *) lfirst(vl); |
| |
| /* Ignore any plain Vars */ |
| if (!IsA(phv, PlaceHolderVar)) |
| continue; |
| |
| /* Create a PlaceHolderInfo entry if there's not one already */ |
| (void) find_placeholder_info(root, phv, true); |
| } |
| list_free(vars); |
| } |
| |
| /* |
| * update_placeholder_eval_levels |
| * Adjust the target evaluation levels for placeholders |
| * |
| * 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 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 |
| * root->join_info_list. It's somewhat annoying to have to do that, but |
| * since we don't have very much information on the placeholders' locations, |
| * it's hard to avoid. Each placeholder's eval_at level must be correct |
| * by the time it starts to figure in outer-join delay decisions for higher |
| * outer joins. |
| * |
| * In future we might want to put additional policy/heuristics here to |
| * try to determine an optimal evaluation level. The current rules will |
| * result in evaluation at the lowest possible level. However, pushing a |
| * placeholder eval up the tree is likely to further constrain evaluation |
| * order for outer joins, so it could easily be counterproductive; and we |
| * don't have enough information at this point to make an intelligent choice. |
| */ |
| void |
| update_placeholder_eval_levels(PlannerInfo *root, SpecialJoinInfo *new_sjinfo) |
| { |
| ListCell *lc1; |
| |
| foreach(lc1, root->placeholder_list) |
| { |
| PlaceHolderInfo *phinfo = (PlaceHolderInfo *) lfirst(lc1); |
| Relids syn_level = phinfo->ph_var->phrels; |
| Relids eval_at; |
| bool found_some; |
| ListCell *lc2; |
| |
| /* |
| * We don't need to do any work on this placeholder unless the |
| * newly-added outer join is syntactically beneath its location. |
| */ |
| if (!bms_is_subset(new_sjinfo->syn_lefthand, syn_level) || |
| !bms_is_subset(new_sjinfo->syn_righthand, syn_level)) |
| continue; |
| |
| /* |
| * Check for delays due to lower outer joins. This is the same logic |
| * as in check_outerjoin_delay in initsplan.c, except that we don't |
| * have anything to do with the delay_upper_joins flags; delay of |
| * upper outer joins will be handled later, based on the eval_at |
| * values we compute now. |
| */ |
| eval_at = phinfo->ph_eval_at; |
| |
| do |
| { |
| found_some = false; |
| foreach(lc2, root->join_info_list) |
| { |
| SpecialJoinInfo *sjinfo = (SpecialJoinInfo *) lfirst(lc2); |
| |
| /* disregard joins not within the PHV's sub-select */ |
| if (!bms_is_subset(sjinfo->syn_lefthand, syn_level) || |
| !bms_is_subset(sjinfo->syn_righthand, syn_level)) |
| continue; |
| |
| /* do we reference any nullable rels of this OJ? */ |
| if (bms_overlap(eval_at, sjinfo->min_righthand) || |
| (sjinfo->jointype == JOIN_FULL && |
| bms_overlap(eval_at, sjinfo->min_lefthand))) |
| { |
| /* yes; have we included all its rels in eval_at? */ |
| if (!bms_is_subset(sjinfo->min_lefthand, eval_at) || |
| !bms_is_subset(sjinfo->min_righthand, eval_at)) |
| { |
| /* no, so add them in */ |
| eval_at = bms_add_members(eval_at, |
| sjinfo->min_lefthand); |
| eval_at = bms_add_members(eval_at, |
| sjinfo->min_righthand); |
| /* we'll need another iteration */ |
| found_some = true; |
| } |
| } |
| } |
| } 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; |
| } |
| } |
| |
| /* |
| * fix_placeholder_input_needed_levels |
| * Adjust the "needed at" levels for placeholder inputs |
| * |
| * 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 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) |
| { |
| ListCell *lc; |
| |
| foreach(lc, root->placeholder_list) |
| { |
| PlaceHolderInfo *phinfo = (PlaceHolderInfo *) lfirst(lc); |
| List *vars = pull_var_clause((Node *) phinfo->ph_var->phexpr, |
| PVC_RECURSE_AGGREGATES | |
| PVC_RECURSE_WINDOWFUNCS | |
| PVC_INCLUDE_PLACEHOLDERS); |
| |
| 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. |
| * |
| * 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 |
| * 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 |
| * do that part until joinrels are formed. |
| */ |
| void |
| add_placeholders_to_base_rels(PlannerInfo *root) |
| { |
| ListCell *lc; |
| |
| foreach(lc, root->placeholder_list) |
| { |
| PlaceHolderInfo *phinfo = (PlaceHolderInfo *) lfirst(lc); |
| Relids eval_at = phinfo->ph_eval_at; |
| int varno; |
| |
| if (bms_get_singleton_member(eval_at, &varno) && |
| bms_nonempty_difference(phinfo->ph_needed, eval_at)) |
| { |
| RelOptInfo *rel = find_base_rel(root, varno); |
| |
| rel->reltarget->exprs = lappend(rel->reltarget->exprs, |
| copyObject(phinfo->ph_var)); |
| /* reltarget's cost and width fields will be updated later */ |
| } |
| } |
| } |
| |
| /* |
| * add_placeholders_to_joinrel |
| * Add any required PlaceHolderVars to a join rel's targetlist; |
| * and if they contain lateral references, add those references to the |
| * joinrel's direct_lateral_relids. |
| * |
| * A join rel should emit a PlaceHolderVar if (a) the PHV can be computed |
| * at or below this join level and (b) the PHV is needed above this level. |
| * However, condition (a) is sufficient to add to direct_lateral_relids, |
| * as explained below. |
| */ |
| void |
| add_placeholders_to_joinrel(PlannerInfo *root, RelOptInfo *joinrel, |
| RelOptInfo *outer_rel, RelOptInfo *inner_rel) |
| { |
| Relids relids = joinrel->relids; |
| ListCell *lc; |
| |
| foreach(lc, root->placeholder_list) |
| { |
| PlaceHolderInfo *phinfo = (PlaceHolderInfo *) lfirst(lc); |
| |
| /* Is it computable here? */ |
| if (bms_is_subset(phinfo->ph_eval_at, relids)) |
| { |
| /* Is it still needed above this joinrel? */ |
| if (bms_nonempty_difference(phinfo->ph_needed, relids)) |
| { |
| /* Yup, add it to the output */ |
| joinrel->reltarget->exprs = lappend(joinrel->reltarget->exprs, |
| phinfo->ph_var); |
| joinrel->reltarget->width += phinfo->ph_width; |
| |
| /* |
| * Charge the cost of evaluating the contained expression if |
| * the PHV can be computed here but not in either input. This |
| * is a bit bogus because we make the decision based on the |
| * first pair of possible input relations considered for the |
| * joinrel. With other pairs, it might be possible to compute |
| * the PHV in one input or the other, and then we'd be double |
| * charging the PHV's cost for some join paths. For now, live |
| * with that; but we might want to improve it later by |
| * refiguring the reltarget costs for each pair of inputs. |
| */ |
| if (!bms_is_subset(phinfo->ph_eval_at, outer_rel->relids) && |
| !bms_is_subset(phinfo->ph_eval_at, inner_rel->relids)) |
| { |
| QualCost cost; |
| |
| cost_qual_eval_node(&cost, (Node *) phinfo->ph_var->phexpr, |
| root); |
| joinrel->reltarget->cost.startup += cost.startup; |
| joinrel->reltarget->cost.per_tuple += cost.per_tuple; |
| } |
| } |
| |
| /* |
| * Also adjust joinrel's direct_lateral_relids to include the |
| * PHV's source rel(s). We must do this even if we're not |
| * actually going to emit the PHV, otherwise join_is_legal() will |
| * reject valid join orderings. (In principle maybe we could |
| * instead remove the joinrel's lateral_relids dependency; but |
| * that's complicated to get right, and cases where we're not |
| * going to emit the PHV are too rare to justify the work.) |
| * |
| * In principle we should only do this if the join doesn't yet |
| * include the PHV's source rel(s). But our caller |
| * build_join_rel() will clean things up by removing the join's |
| * own relids from its direct_lateral_relids, so we needn't |
| * account for that here. |
| */ |
| joinrel->direct_lateral_relids = |
| bms_add_members(joinrel->direct_lateral_relids, |
| phinfo->ph_lateral); |
| } |
| } |
| } |
| |
| /* |
| * In GPDB, SubPlans expressions pose a problem, if they're moved or |
| * duplicated in the plan tree. If the SubPlan contains any Motions, it can |
| * only be evaluated at a particular slice, because the Motion nodes in the |
| * SubPlan are set up to send the result to the parent slice. To prevent |
| * multiple evaluation of SubPlans, we wrap all SubPlan references in |
| * PlaceHolderVars. When wrapped in a PlaceHolderVar, the SubPlan gets |
| * evaluated once, at the lowest level in the join tree where possible, and |
| * if it's needed elsewhere, its value is propagated up the plan tree, in |
| * the target lists of all the nodes. |
| * |
| * This is more conservative than necessary. It would be OK to evaluate a |
| * SubPlan multiple times, as long as all the evaluations happen in the same |
| * slice. However, we don't divide the plan into slices until much later in |
| * planning, so we don't know that yet. It's quite possibly cheaper to avoid |
| * multiple evaluation anyway, since subqueries tend be pretty expensive. |
| * (On the other hand, PlaceHolderVars can constrain the join order if there |
| * are outer joins in the query.) |
| */ |
| typedef struct |
| { |
| PlannerInfo *root; |
| Relids phrels; /* current syntactic location (as a set of baserels) |
| * in the join tree. */ |
| } make_placeholders_for_subplans_in_expr_context; |
| |
| static bool |
| subplan_needs_placeholder(PlannerInfo *root, SubPlan *spexpr) |
| { |
| Plan *subplan; |
| |
| /* InitPlans should be converted to Params by now. */ |
| if (spexpr->is_initplan) |
| elog(ERROR, "unexpected Init Plan in plan tree"); |
| |
| /* |
| * We don't need PlaceHolderVars for plans that are readily executable |
| * anywhere. |
| * |
| * XXX: This isn't exactly what we are worried about here. It's |
| * theoretically possible that the Plan tree contains branches with |
| * Motions, but the topmost node is General. I don't think the planner |
| * can produce such plans at the moment, though. |
| */ |
| subplan = planner_subplan_get_plan(root, spexpr); |
| if (subplan->flow->locustype == CdbLocusType_General) |
| return false; |
| |
| return true; |
| } |
| |
| static Node * |
| make_placeholders_for_subplans_in_expr_mutator(Node *expr, void *context) |
| { |
| make_placeholders_for_subplans_in_expr_context *cxt = |
| (make_placeholders_for_subplans_in_expr_context *) context; |
| bool placeholder_needed = false; |
| |
| if (expr == NULL) |
| return NULL; |
| if (IsA(expr, SubPlan)) |
| { |
| SubPlan *spexpr = (SubPlan *) expr; |
| |
| if (subplan_needs_placeholder(cxt->root, spexpr)) |
| placeholder_needed = true; |
| } |
| /* |
| * An AlternativeSubPlan is wrapped in a PlaceHolder, if any of the |
| * alternative SubPlans need it. (It's probably not possible for only |
| * some of them to need it, but this seems like the right thing to do, |
| * if it was.) |
| */ |
| else if (IsA(expr, AlternativeSubPlan)) |
| { |
| AlternativeSubPlan *asp = (AlternativeSubPlan *) expr; |
| ListCell *lc; |
| |
| foreach(lc, asp->subplans) |
| { |
| SubPlan *spexpr = (SubPlan *) lfirst(lc); |
| |
| Assert(IsA(spexpr, SubPlan)); |
| |
| if (subplan_needs_placeholder(cxt->root, spexpr)) |
| { |
| placeholder_needed = true; |
| break; |
| } |
| } |
| } |
| |
| if (placeholder_needed) |
| return (Node *) make_placeholder_expr(cxt->root, (Expr *) expr, cxt->phrels); |
| else |
| return expression_tree_mutator(expr, make_placeholders_for_subplans_in_expr_mutator, context); |
| } |
| |
| static Node * |
| make_placeholders_for_subplans_in_expr(PlannerInfo *root, Node *expr, Relids phrels) |
| { |
| make_placeholders_for_subplans_in_expr_context cxt; |
| |
| cxt.root = root; |
| cxt.phrels = phrels; |
| |
| return make_placeholders_for_subplans_in_expr_mutator(expr, &cxt); |
| } |
| |
| /* |
| * The logic to track 'scope' is copied from deconstruct_jointree. We cannot |
| * do this in deconstruct_jointree(), because it's too late to assign new |
| * placeholders there. |
| */ |
| static void |
| make_placeholders_for_subplans_recurse(PlannerInfo *root, Node *jtnode, Relids *qualscope) |
| { |
| if (jtnode == NULL) |
| { |
| *qualscope = NULL; |
| return; |
| } |
| if (IsA(jtnode, RangeTblRef)) |
| { |
| int varno = ((RangeTblRef *) jtnode)->rtindex; |
| |
| /* No quals to deal with here */ |
| *qualscope = bms_make_singleton(varno); |
| } |
| else if (IsA(jtnode, FromExpr)) |
| { |
| FromExpr *f = (FromExpr *) jtnode; |
| ListCell *l; |
| |
| /* |
| * First, recurse to handle child joins. |
| */ |
| *qualscope = NULL; |
| foreach(l, f->fromlist) |
| { |
| Relids sub_qualscope; |
| |
| make_placeholders_for_subplans_recurse(root, lfirst(l), &sub_qualscope); |
| *qualscope = bms_add_members(*qualscope, sub_qualscope); |
| } |
| |
| /* |
| * Now process the top-level quals. |
| */ |
| f->quals = make_placeholders_for_subplans_in_expr(root, f->quals, *qualscope); |
| } |
| else if (IsA(jtnode, JoinExpr)) |
| { |
| JoinExpr *j = (JoinExpr *) jtnode; |
| Relids leftids = NULL; |
| Relids rightids = NULL; |
| |
| /* |
| * First, recurse to handle child joins. |
| */ |
| make_placeholders_for_subplans_recurse(root, j->larg, &leftids); |
| make_placeholders_for_subplans_recurse(root, j->rarg, &rightids); |
| |
| *qualscope = bms_union(leftids, rightids); |
| |
| /* Process the qual clauses */ |
| j->quals = make_placeholders_for_subplans_in_expr(root, j->quals, *qualscope); |
| } |
| else |
| elog(ERROR, "unrecognized node type: %d", |
| (int) nodeTag(jtnode)); |
| } |
| |
| void |
| make_placeholders_for_subplans(PlannerInfo *root) |
| { |
| Relids scope; |
| |
| make_placeholders_for_subplans_recurse(root, (Node *) root->parse->jointree, &scope); |
| } |