diff options
Diffstat (limited to 'src/backend/optimizer/plan')
-rw-r--r-- | src/backend/optimizer/plan/createplan.c | 111 | ||||
-rw-r--r-- | src/backend/optimizer/plan/initsplan.c | 62 | ||||
-rw-r--r-- | src/backend/optimizer/plan/planner.c | 3 | ||||
-rw-r--r-- | src/backend/optimizer/plan/subselect.c | 1 |
4 files changed, 174 insertions, 3 deletions
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index 414406bb8a1..6bb821fb385 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -84,6 +84,7 @@ static HashJoin *create_hashjoin_plan(PlannerInfo *root, HashPath *best_path, Plan *outer_plan, Plan *inner_plan); static Node *replace_nestloop_params(PlannerInfo *root, Node *expr); static Node *replace_nestloop_params_mutator(Node *node, PlannerInfo *root); +static void identify_nestloop_extparams(PlannerInfo *root, Plan *subplan); static List *fix_indexqual_references(PlannerInfo *root, IndexPath *index_path); static List *fix_indexorderby_references(PlannerInfo *root, IndexPath *index_path); static Node *fix_indexqual_operand(Node *node, IndexOptInfo *index, int indexcol); @@ -1640,6 +1641,7 @@ create_subqueryscan_plan(PlannerInfo *root, Path *best_path, { scan_clauses = (List *) replace_nestloop_params(root, (Node *) scan_clauses); + identify_nestloop_extparams(root, best_path->parent->subplan); } scan_plan = make_subqueryscan(tlist, @@ -1664,11 +1666,13 @@ create_functionscan_plan(PlannerInfo *root, Path *best_path, FunctionScan *scan_plan; Index scan_relid = best_path->parent->relid; RangeTblEntry *rte; + Node *funcexpr; /* it should be a function base rel... */ Assert(scan_relid > 0); rte = planner_rt_fetch(scan_relid, root); Assert(rte->rtekind == RTE_FUNCTION); + funcexpr = rte->funcexpr; /* Sort clauses into best execution order */ scan_clauses = order_qual_clauses(root, scan_clauses); @@ -1676,8 +1680,17 @@ create_functionscan_plan(PlannerInfo *root, Path *best_path, /* Reduce RestrictInfo list to bare expressions; ignore pseudoconstants */ scan_clauses = extract_actual_clauses(scan_clauses, false); + /* Replace any outer-relation variables with nestloop params */ + if (best_path->param_info) + { + scan_clauses = (List *) + replace_nestloop_params(root, (Node *) scan_clauses); + /* The func expression itself could contain nestloop params, too */ + funcexpr = replace_nestloop_params(root, funcexpr); + } + scan_plan = make_functionscan(tlist, scan_clauses, scan_relid, - rte->funcexpr, + funcexpr, rte->eref->colnames, rte->funccoltypes, rte->funccoltypmods, @@ -2560,6 +2573,102 @@ replace_nestloop_params_mutator(Node *node, PlannerInfo *root) } /* + * identify_nestloop_extparams + * Identify extParams of a parameterized subquery that need to be fed + * from an outer nestloop. + * + * The subplan's references to the outer variables are already represented + * as PARAM_EXEC Params, so we need not modify the subplan here. What we + * do need to do is add entries to root->curOuterParams to signal the parent + * nestloop plan node that it must provide these values. + */ +static void +identify_nestloop_extparams(PlannerInfo *root, Plan *subplan) +{ + Bitmapset *tmpset; + int paramid; + + /* Examine each extParam of the subquery's plan */ + tmpset = bms_copy(subplan->extParam); + while ((paramid = bms_first_member(tmpset)) >= 0) + { + PlannerParamItem *pitem = list_nth(root->glob->paramlist, paramid); + + /* Ignore anything coming from an upper query level */ + if (pitem->abslevel != root->query_level) + continue; + + if (IsA(pitem->item, Var)) + { + Var *var = (Var *) pitem->item; + NestLoopParam *nlp; + ListCell *lc; + + /* If not from a nestloop outer rel, nothing to do */ + if (!bms_is_member(var->varno, root->curOuterRels)) + continue; + /* Is this param already listed in root->curOuterParams? */ + foreach(lc, root->curOuterParams) + { + nlp = (NestLoopParam *) lfirst(lc); + if (nlp->paramno == paramid) + { + Assert(equal(var, nlp->paramval)); + /* Present, so nothing to do */ + break; + } + } + if (lc == NULL) + { + /* No, so add it */ + nlp = makeNode(NestLoopParam); + nlp->paramno = paramid; + nlp->paramval = copyObject(var); + root->curOuterParams = lappend(root->curOuterParams, nlp); + } + } + else if (IsA(pitem->item, PlaceHolderVar)) + { + PlaceHolderVar *phv = (PlaceHolderVar *) pitem->item; + NestLoopParam *nlp; + ListCell *lc; + + /* + * If not from a nestloop outer rel, nothing to do. 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)) + continue; + if (!bms_is_subset(find_placeholder_info(root, phv, false)->ph_eval_at, + root->curOuterRels)) + continue; + /* Is this param already listed in root->curOuterParams? */ + foreach(lc, root->curOuterParams) + { + nlp = (NestLoopParam *) lfirst(lc); + if (nlp->paramno == paramid) + { + Assert(equal(phv, nlp->paramval)); + /* Present, so nothing to do */ + break; + } + } + if (lc == NULL) + { + /* No, so add it */ + nlp = makeNode(NestLoopParam); + nlp->paramno = paramid; + nlp->paramval = copyObject(phv); + root->curOuterParams = lappend(root->curOuterParams, nlp); + } + } + } + bms_free(tmpset); +} + +/* * fix_indexqual_references * Adjust indexqual clauses to the form the executor's indexqual * machinery needs. diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c index 3c7fa632b8e..4481db5c341 100644 --- a/src/backend/optimizer/plan/initsplan.c +++ b/src/backend/optimizer/plan/initsplan.c @@ -204,6 +204,64 @@ add_vars_to_targetlist(PlannerInfo *root, List *vars, } } +/* + * 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. + * + * 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? + */ +static void +extract_lateral_references(PlannerInfo *root, int rtindex) +{ + RangeTblEntry *rte = root->simple_rte_array[rtindex]; + List *vars; + List *newvars; + Relids where_needed; + ListCell *lc; + + /* No cross-references are possible if it's not LATERAL */ + if (!rte->lateral) + return; + + /* Fetch the appropriate variables */ + if (rte->rtekind == RTE_SUBQUERY) + vars = pull_vars_of_level((Node *) rte->subquery, 1); + else if (rte->rtekind == RTE_FUNCTION) + vars = pull_vars_of_level(rte->funcexpr, 0); + else + return; + + /* Copy each Var and adjust it to match our level */ + newvars = NIL; + foreach(lc, vars) + { + Var *var = (Var *) lfirst(lc); + + var = copyObject(var); + var->varlevelsup = 0; + newvars = lappend(newvars, var); + } + + /* + * 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 + * at the join of the LATERAL RTE with its source RTE. But it will work, + * and it's much less tedious than computing a separate where_needed for + * each Var. + */ + where_needed = bms_make_singleton(rtindex); + + /* Push the Vars into their source relations' targetlists */ + add_vars_to_targetlist(root, newvars, where_needed, false); + + list_free(newvars); + list_free(vars); +} + /***************************************************************************** * @@ -286,7 +344,9 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join, { int varno = ((RangeTblRef *) jtnode)->rtindex; - /* No quals to deal with, just return correct result */ + /* No quals to deal with, but do check for LATERAL subqueries */ + extract_lateral_references(root, varno); + /* Result qualscope is just the one Relid */ *qualscope = bms_make_singleton(varno); /* A single baserel does not create an inner join */ *inner_join_rels = NULL; diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 31fe5570723..26b5dbb559d 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -1817,7 +1817,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) * * Once grouping_planner() has applied a general tlist to the topmost * scan/join plan node, any tlist eval cost for added-on nodes should be - * accounted for as we create those nodes. Presently, of the node types we + * accounted for as we create those nodes. Presently, of the node types we * can add on later, only Agg, WindowAgg, and Group project new tlists (the * rest just copy their input tuples) --- so make_agg(), make_windowagg() and * make_group() are responsible for calling this function to account for their @@ -3257,6 +3257,7 @@ plan_cluster_use_sort(Oid tableOid, Oid indexOid) rte->rtekind = RTE_RELATION; rte->relid = tableOid; rte->relkind = RELKIND_RELATION; + rte->lateral = false; rte->inh = false; rte->inFromCl = true; query->rtable = list_make1(rte); diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index 8ce6bee8561..863c943f2a0 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -1231,6 +1231,7 @@ convert_ANY_sublink_to_join(PlannerInfo *root, SubLink *sublink, rte = addRangeTableEntryForSubquery(NULL, subselect, makeAlias("ANY_subquery", NIL), + false, false); parse->rtable = lappend(parse->rtable, rte); rtindex = list_length(parse->rtable); |